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 #include <memory>
      6 #include <utility>
      7 #include <vector>
      8 
      9 #include "core/fpdfapi/parser/cpdf_dictionary.h"
     10 #include "core/fpdfapi/parser/cpdf_name.h"
     11 #include "core/fpdfapi/parser/cpdf_number.h"
     12 #include "core/fpdfapi/parser/cpdf_stream.h"
     13 #include "core/fpdfapi/parser/cpdf_string.h"
     14 #include "core/fpdfdoc/cpdf_filespec.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 #include "testing/test_support.h"
     17 #include "third_party/base/ptr_util.h"
     18 
     19 TEST(cpdf_filespec, EncodeDecodeFileName) {
     20   static const std::vector<pdfium::NullTermWstrFuncTestData> test_data = {
     21     // Empty src string.
     22     {L"", L""},
     23     // only file name.
     24     {L"test.pdf", L"test.pdf"},
     25 #if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
     26     // With drive identifier.
     27     {L"r:\\pdfdocs\\spec.pdf", L"/r/pdfdocs/spec.pdf"},
     28     // Relative path.
     29     {L"My Document\\test.pdf", L"My Document/test.pdf"},
     30     // Absolute path without drive identifier.
     31     {L"\\pdfdocs\\spec.pdf", L"//pdfdocs/spec.pdf"},
     32     // Absolute path with double backslashes.
     33     {L"\\\\pdfdocs\\spec.pdf", L"/pdfdocs/spec.pdf"},
     34 // Network resource name. It is not supported yet.
     35 // {L"pclib/eng:\\pdfdocs\\spec.pdf", L"/pclib/eng/pdfdocs/spec.pdf"},
     36 #elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
     37     // Absolute path with colon separator.
     38     {L"Mac HD:PDFDocs:spec.pdf", L"/Mac HD/PDFDocs/spec.pdf"},
     39     // Relative path with colon separator.
     40     {L"PDFDocs:spec.pdf", L"PDFDocs/spec.pdf"},
     41 #else
     42     // Relative path.
     43     {L"./docs/test.pdf", L"./docs/test.pdf"},
     44     // Relative path with parent dir.
     45     {L"../test_docs/test.pdf", L"../test_docs/test.pdf"},
     46     // Absolute path.
     47     {L"/usr/local/home/test.pdf", L"/usr/local/home/test.pdf"},
     48 #endif
     49   };
     50   for (const auto& data : test_data) {
     51     EXPECT_STREQ(data.expected,
     52                  CPDF_FileSpec::EncodeFileName(data.input).c_str());
     53     // DecodeFileName is the reverse procedure of EncodeFileName.
     54     EXPECT_STREQ(data.input,
     55                  CPDF_FileSpec::DecodeFileName(data.expected).c_str());
     56   }
     57 }
     58 
     59 TEST(cpdf_filespec, GetFileName) {
     60   {
     61     // String object.
     62     static const pdfium::NullTermWstrFuncTestData test_data = {
     63 #if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
     64       L"/C/docs/test.pdf",
     65       L"C:\\docs\\test.pdf"
     66 #elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
     67       L"/Mac HD/docs/test.pdf",
     68       L"Mac HD:docs:test.pdf"
     69 #else
     70       L"/docs/test.pdf",
     71       L"/docs/test.pdf"
     72 #endif
     73     };
     74     auto str_obj = pdfium::MakeUnique<CPDF_String>(nullptr, test_data.input);
     75     CPDF_FileSpec file_spec(str_obj.get());
     76     EXPECT_STREQ(test_data.expected, file_spec.GetFileName().c_str());
     77   }
     78   {
     79     // Dictionary object.
     80     static const pdfium::NullTermWstrFuncTestData test_data[] = {
     81 #if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
     82       {L"/C/docs/test.pdf", L"C:\\docs\\test.pdf"},
     83       {L"/D/docs/test.pdf", L"D:\\docs\\test.pdf"},
     84       {L"/E/docs/test.pdf", L"E:\\docs\\test.pdf"},
     85       {L"/F/docs/test.pdf", L"F:\\docs\\test.pdf"},
     86       {L"/G/docs/test.pdf", L"G:\\docs\\test.pdf"},
     87 #elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
     88       {L"/Mac HD/docs1/test.pdf", L"Mac HD:docs1:test.pdf"},
     89       {L"/Mac HD/docs2/test.pdf", L"Mac HD:docs2:test.pdf"},
     90       {L"/Mac HD/docs3/test.pdf", L"Mac HD:docs3:test.pdf"},
     91       {L"/Mac HD/docs4/test.pdf", L"Mac HD:docs4:test.pdf"},
     92       {L"/Mac HD/docs5/test.pdf", L"Mac HD:docs5:test.pdf"},
     93 #else
     94       {L"/docs/a/test.pdf", L"/docs/a/test.pdf"},
     95       {L"/docs/b/test.pdf", L"/docs/b/test.pdf"},
     96       {L"/docs/c/test.pdf", L"/docs/c/test.pdf"},
     97       {L"/docs/d/test.pdf", L"/docs/d/test.pdf"},
     98       {L"/docs/e/test.pdf", L"/docs/e/test.pdf"},
     99 #endif
    100     };
    101     // Keyword fields in reverse order of precedence to retrieve the file name.
    102     const char* const keywords[] = {"Unix", "Mac", "DOS", "F", "UF"};
    103     static_assert(FX_ArraySize(test_data) == FX_ArraySize(keywords),
    104                   "size mismatch");
    105     auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
    106     CPDF_FileSpec file_spec(dict_obj.get());
    107     EXPECT_TRUE(file_spec.GetFileName().IsEmpty());
    108     for (size_t i = 0; i < FX_ArraySize(keywords); ++i) {
    109       dict_obj->SetNewFor<CPDF_String>(keywords[i], test_data[i].input);
    110       EXPECT_STREQ(test_data[i].expected, file_spec.GetFileName().c_str());
    111     }
    112 
    113     // With all the former fields and 'FS' field suggests 'URL' type.
    114     dict_obj->SetNewFor<CPDF_String>("FS", "URL", false);
    115     // Url string is not decoded.
    116     EXPECT_STREQ(test_data[4].input, file_spec.GetFileName().c_str());
    117   }
    118   {
    119     // Invalid object.
    120     auto name_obj = pdfium::MakeUnique<CPDF_Name>(nullptr, "test.pdf");
    121     CPDF_FileSpec file_spec(name_obj.get());
    122     EXPECT_TRUE(file_spec.GetFileName().IsEmpty());
    123   }
    124 }
    125 
    126 TEST(cpdf_filespec, SetFileName) {
    127   static const pdfium::NullTermWstrFuncTestData test_data = {
    128 #if _FX_PLATFORM_ == _FX_PLATFORM_WINDOWS_
    129     L"C:\\docs\\test.pdf",
    130     L"/C/docs/test.pdf"
    131 #elif _FX_PLATFORM_ == _FX_PLATFORM_APPLE_
    132     L"Mac HD:docs:test.pdf",
    133     L"/Mac HD/docs/test.pdf"
    134 #else
    135     L"/docs/test.pdf",
    136     L"/docs/test.pdf"
    137 #endif
    138   };
    139   // String object.
    140   auto str_obj = pdfium::MakeUnique<CPDF_String>(nullptr, L"babababa");
    141   CPDF_FileSpec file_spec1(str_obj.get());
    142   file_spec1.SetFileName(test_data.input);
    143   // Check internal object value.
    144   EXPECT_STREQ(test_data.expected, str_obj->GetUnicodeText().c_str());
    145   // Check we can get the file name back.
    146   EXPECT_STREQ(test_data.input, file_spec1.GetFileName().c_str());
    147 
    148   // Dictionary object.
    149   auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
    150   CPDF_FileSpec file_spec2(dict_obj.get());
    151   file_spec2.SetFileName(test_data.input);
    152   // Check internal object value.
    153   EXPECT_STREQ(test_data.expected, dict_obj->GetUnicodeTextFor("F").c_str());
    154   EXPECT_STREQ(test_data.expected, dict_obj->GetUnicodeTextFor("UF").c_str());
    155   // Check we can get the file name back.
    156   EXPECT_STREQ(test_data.input, file_spec2.GetFileName().c_str());
    157 }
    158 
    159 TEST(cpdf_filespec, GetFileStream) {
    160   {
    161     // Invalid object.
    162     auto name_obj = pdfium::MakeUnique<CPDF_Name>(nullptr, "test.pdf");
    163     CPDF_FileSpec file_spec(name_obj.get());
    164     EXPECT_FALSE(file_spec.GetFileStream());
    165   }
    166   {
    167     // Dictionary object missing its embedded files dictionary.
    168     auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
    169     CPDF_FileSpec file_spec(dict_obj.get());
    170     EXPECT_FALSE(file_spec.GetFileStream());
    171   }
    172   {
    173     // Dictionary object with an empty embedded files dictionary.
    174     auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
    175     dict_obj->SetNewFor<CPDF_Dictionary>("EF");
    176     CPDF_FileSpec file_spec(dict_obj.get());
    177     EXPECT_FALSE(file_spec.GetFileStream());
    178   }
    179   {
    180     // Dictionary object with a non-empty embedded files dictionary.
    181     auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
    182     dict_obj->SetNewFor<CPDF_Dictionary>("EF");
    183     CPDF_FileSpec file_spec(dict_obj.get());
    184 
    185     const wchar_t file_name[] = L"test.pdf";
    186     const char* const keys[] = {"Unix", "Mac", "DOS", "F", "UF"};
    187     const char* const streams[] = {"test1", "test2", "test3", "test4", "test5"};
    188     static_assert(FX_ArraySize(keys) == FX_ArraySize(streams), "size mismatch");
    189     CPDF_Dictionary* file_dict =
    190         file_spec.GetObj()->AsDictionary()->GetDictFor("EF");
    191 
    192     // Keys in reverse order of precedence to retrieve the file content stream.
    193     for (size_t i = 0; i < FX_ArraySize(keys); ++i) {
    194       // Set the file name.
    195       dict_obj->SetNewFor<CPDF_String>(keys[i], file_name);
    196 
    197       // Set the file stream.
    198       auto pDict = pdfium::MakeUnique<CPDF_Dictionary>();
    199       size_t buf_len = strlen(streams[i]) + 1;
    200       std::unique_ptr<uint8_t, FxFreeDeleter> buf(FX_Alloc(uint8_t, buf_len));
    201       memcpy(buf.get(), streams[i], buf_len);
    202       file_dict->SetNewFor<CPDF_Stream>(keys[i], std::move(buf), buf_len,
    203                                         std::move(pDict));
    204 
    205       // Check that the file content stream is as expected.
    206       EXPECT_STREQ(
    207           streams[i],
    208           file_spec.GetFileStream()->GetUnicodeText().UTF8Encode().c_str());
    209 
    210       if (i == 2) {
    211         dict_obj->SetNewFor<CPDF_String>("FS", "URL", false);
    212         EXPECT_FALSE(file_spec.GetFileStream());
    213       }
    214     }
    215   }
    216 }
    217 
    218 TEST(cpdf_filespec, GetParamsDict) {
    219   {
    220     // Invalid object.
    221     auto name_obj = pdfium::MakeUnique<CPDF_Name>(nullptr, "test.pdf");
    222     CPDF_FileSpec file_spec(name_obj.get());
    223     EXPECT_FALSE(file_spec.GetParamsDict());
    224   }
    225   {
    226     // Dictionary object.
    227     auto dict_obj = pdfium::MakeUnique<CPDF_Dictionary>();
    228     dict_obj->SetNewFor<CPDF_Dictionary>("EF");
    229     dict_obj->SetNewFor<CPDF_String>("UF", L"test.pdf");
    230     CPDF_FileSpec file_spec(dict_obj.get());
    231     EXPECT_FALSE(file_spec.GetParamsDict());
    232 
    233     // Add a file stream to the embedded files dictionary.
    234     CPDF_Dictionary* file_dict =
    235         file_spec.GetObj()->AsDictionary()->GetDictFor("EF");
    236     auto pDict = pdfium::MakeUnique<CPDF_Dictionary>();
    237     std::unique_ptr<uint8_t, FxFreeDeleter> buf(FX_Alloc(uint8_t, 6));
    238     memcpy(buf.get(), "hello", 6);
    239     file_dict->SetNewFor<CPDF_Stream>("UF", std::move(buf), 6,
    240                                       std::move(pDict));
    241 
    242     // Add a params dictionary to the file stream.
    243     CPDF_Stream* stream = file_dict->GetStreamFor("UF");
    244     CPDF_Dictionary* stream_dict = stream->GetDict();
    245     stream_dict->SetNewFor<CPDF_Dictionary>("Params");
    246     EXPECT_TRUE(file_spec.GetParamsDict());
    247 
    248     // Add a parameter to the params dictionary.
    249     CPDF_Dictionary* params_dict = stream_dict->GetDictFor("Params");
    250     params_dict->SetNewFor<CPDF_Number>("Size", 6);
    251     EXPECT_EQ(6, file_spec.GetParamsDict()->GetIntegerFor("Size"));
    252   }
    253 }
    254