• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TEST(cpdf_filespec,EncodeDecodeFileName)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 
TEST(cpdf_filespec,GetFileName)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 
TEST(cpdf_filespec,SetFileName)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 
TEST(cpdf_filespec,GetFileStream)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 
TEST(cpdf_filespec,GetParamsDict)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