From dc1aa325c22a3d6cd240efa9d35f9e175fe8455b Mon Sep 17 00:00:00 2001 From: Jane Liu Date: Tue, 11 Jul 2017 15:16:08 -0400 Subject: Added helper functions in the class CPDF_FileSpec 1. Added two helper functions in CPDF_FileSpec, which will be useful when adding support for embedded files: CPDF_FileSpec::GetFileStream() - useful because the stream object contains the file's data CPDF_FileSpec::GetParamsDict() - useful because the params dictionary contains parameters of the file, such as creation date, size, etc. * Added two unit tests testing both functions. Bug=pdfium:174 Change-Id: I33ea21ddb621434007f94767f281ead0b00ecb8a Reviewed-on: https://pdfium-review.googlesource.com/7355 Reviewed-by: dsinclair Reviewed-by: Lei Zhang Commit-Queue: Jane Liu --- core/fpdfdoc/cpdf_filespec.cpp | 49 +++++++++++++++-- core/fpdfdoc/cpdf_filespec.h | 4 ++ core/fpdfdoc/cpdf_filespec_unittest.cpp | 98 +++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 5 deletions(-) diff --git a/core/fpdfdoc/cpdf_filespec.cpp b/core/fpdfdoc/cpdf_filespec.cpp index 59915eaa05..7d1f0e68b9 100644 --- a/core/fpdfdoc/cpdf_filespec.cpp +++ b/core/fpdfdoc/cpdf_filespec.cpp @@ -6,9 +6,12 @@ #include "core/fpdfdoc/cpdf_filespec.h" +#include + #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_name.h" #include "core/fpdfapi/parser/cpdf_object.h" +#include "core/fpdfapi/parser/cpdf_stream.h" #include "core/fpdfapi/parser/cpdf_string.h" #include "core/fpdfapi/parser/fpdf_parser_decode.h" #include "core/fxcrt/fx_system.h" @@ -50,6 +53,12 @@ CFX_WideString ChangeSlashToPDF(const wchar_t* str) { } // namespace +CPDF_FileSpec::CPDF_FileSpec(CPDF_Object* pObj) : m_pObj(pObj) { + ASSERT(m_pObj); +} + +CPDF_FileSpec::~CPDF_FileSpec() {} + CFX_WideString CPDF_FileSpec::DecodeFileName(const CFX_WideString& filepath) { if (filepath.GetLength() <= 1) return CFX_WideString(); @@ -112,9 +121,42 @@ bool CPDF_FileSpec::GetFileName(CFX_WideString* csFileName) const { return true; } -CPDF_FileSpec::CPDF_FileSpec(CPDF_Object* pObj) : m_pObj(pObj) {} +CPDF_Stream* CPDF_FileSpec::GetFileStream() const { + CPDF_Dictionary* pDict = m_pObj->AsDictionary(); + if (!pDict) + return nullptr; + + // Get the embedded files dictionary. + CPDF_Dictionary* pFiles = pDict->GetDictFor("EF"); + if (!pFiles) + return nullptr; + + // Get the file stream of the highest precedence with its file specification + // string available. Follows the same precedence order as GetFileName(). + constexpr const char* keys[] = {"UF", "F", "DOS", "Mac", "Unix"}; + size_t end = pDict->GetStringFor("FS") == "URL" ? 2 : FX_ArraySize(keys); + for (size_t i = 0; i < end; ++i) { + const CFX_ByteString& key = keys[i]; + if (!pDict->GetUnicodeTextFor(key).IsEmpty()) { + CPDF_Stream* pStream = pFiles->GetStreamFor(key); + if (pStream) + return pStream; + } + } + return nullptr; +} -CPDF_FileSpec::~CPDF_FileSpec() {} +CPDF_Dictionary* CPDF_FileSpec::GetParamsDict() const { + CPDF_Stream* pStream = GetFileStream(); + if (!pStream) + return nullptr; + + CPDF_Dictionary* pDict = pStream->GetDict(); + if (!pDict) + return nullptr; + + return pDict->GetDictFor("Params"); +} CFX_WideString CPDF_FileSpec::EncodeFileName(const CFX_WideString& filepath) { if (filepath.GetLength() <= 1) @@ -146,9 +188,6 @@ CFX_WideString CPDF_FileSpec::EncodeFileName(const CFX_WideString& filepath) { } void CPDF_FileSpec::SetFileName(const CFX_WideString& wsFileName) { - if (!m_pObj) - return; - CFX_WideString wsStr = EncodeFileName(wsFileName); if (m_pObj->IsString()) { m_pObj->SetString(CFX_ByteString::FromUnicode(wsStr)); diff --git a/core/fpdfdoc/cpdf_filespec.h b/core/fpdfdoc/cpdf_filespec.h index 04baf2e63f..2cef20b48a 100644 --- a/core/fpdfdoc/cpdf_filespec.h +++ b/core/fpdfdoc/cpdf_filespec.h @@ -12,7 +12,9 @@ #include "core/fxcrt/cfx_weak_ptr.h" #include "core/fxcrt/fx_string.h" +class CPDF_Dictionary; class CPDF_Object; +class CPDF_Stream; class CPDF_FileSpec { public: @@ -27,6 +29,8 @@ class CPDF_FileSpec { CPDF_Object* GetObj() const { return m_pObj.Get(); } bool GetFileName(CFX_WideString* wsFileName) const; + CPDF_Stream* GetFileStream() const; + CPDF_Dictionary* GetParamsDict() const; // Set this file spec to refer to a file name (not a url). void SetFileName(const CFX_WideString& wsFileName); diff --git a/core/fpdfdoc/cpdf_filespec_unittest.cpp b/core/fpdfdoc/cpdf_filespec_unittest.cpp index e237aa5b6b..7e2975c5f0 100644 --- a/core/fpdfdoc/cpdf_filespec_unittest.cpp +++ b/core/fpdfdoc/cpdf_filespec_unittest.cpp @@ -3,10 +3,13 @@ // found in the LICENSE file. #include +#include #include #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_name.h" +#include "core/fpdfapi/parser/cpdf_number.h" +#include "core/fpdfapi/parser/cpdf_stream.h" #include "core/fpdfapi/parser/cpdf_string.h" #include "core/fpdfdoc/cpdf_filespec.h" #include "testing/gtest/include/gtest/gtest.h" @@ -161,3 +164,98 @@ TEST(cpdf_filespec, SetFileName) { EXPECT_TRUE(file_spec2.GetFileName(&file_name)); EXPECT_TRUE(file_name == test_data.input); } + +TEST(cpdf_filespec, GetFileStream) { + { + // Invalid object. + auto name_obj = pdfium::MakeUnique(nullptr, "test.pdf"); + CPDF_FileSpec file_spec(name_obj.get()); + EXPECT_FALSE(file_spec.GetFileStream()); + } + { + // Dictionary object missing its embedded files dictionary. + auto dict_obj = pdfium::MakeUnique(); + CPDF_FileSpec file_spec(dict_obj.get()); + EXPECT_FALSE(file_spec.GetFileStream()); + } + { + // Dictionary object with an empty embedded files dictionary. + auto dict_obj = pdfium::MakeUnique(); + dict_obj->SetNewFor("EF"); + CPDF_FileSpec file_spec(dict_obj.get()); + EXPECT_FALSE(file_spec.GetFileStream()); + } + { + // Dictionary object with a non-empty embedded files dictionary. + auto dict_obj = pdfium::MakeUnique(); + dict_obj->SetNewFor("EF"); + CPDF_FileSpec file_spec(dict_obj.get()); + + const char* const keys[5] = {"Unix", "Mac", "DOS", "F", "UF"}; + const wchar_t file_name[] = L"test.pdf"; + const char* const stream[5] = {"test1", "test2", "test3", "test4", "test5"}; + CPDF_Dictionary* file_dict = + file_spec.GetObj()->AsDictionary()->GetDictFor("EF"); + + // Keys in reverse order of precedence to retrieve the file content stream. + for (size_t i = 0; i < FX_ArraySize(keys); ++i) { + // Set the file name. + dict_obj->SetNewFor(keys[i], file_name); + + // Set the file stream. + auto pDict = pdfium::MakeUnique(); + size_t buf_len = strlen(stream[i]) + 1; + std::unique_ptr buf(FX_Alloc(uint8_t, buf_len)); + memcpy(buf.get(), stream[i], buf_len); + file_dict->SetNewFor(keys[i], std::move(buf), buf_len, + std::move(pDict)); + + // Check that the file content stream is as expected. + EXPECT_STREQ( + stream[i], + file_spec.GetFileStream()->GetUnicodeText().UTF8Encode().c_str()); + + if (i == 2) { + dict_obj->SetNewFor("FS", "URL", false); + EXPECT_FALSE(file_spec.GetFileStream()); + } + } + } +} + +TEST(cpdf_filespec, GetParamsDict) { + { + // Invalid object. + auto name_obj = pdfium::MakeUnique(nullptr, "test.pdf"); + CPDF_FileSpec file_spec(name_obj.get()); + EXPECT_FALSE(file_spec.GetParamsDict()); + } + { + // Dictionary object. + auto dict_obj = pdfium::MakeUnique(); + dict_obj->SetNewFor("EF"); + dict_obj->SetNewFor("UF", L"test.pdf"); + CPDF_FileSpec file_spec(dict_obj.get()); + EXPECT_FALSE(file_spec.GetParamsDict()); + + // Add a file stream to the embedded files dictionary. + CPDF_Dictionary* file_dict = + file_spec.GetObj()->AsDictionary()->GetDictFor("EF"); + auto pDict = pdfium::MakeUnique(); + std::unique_ptr buf(FX_Alloc(uint8_t, 6)); + memcpy(buf.get(), "hello", 6); + file_dict->SetNewFor("UF", std::move(buf), 6, + std::move(pDict)); + + // Add a params dictionary to the file stream. + CPDF_Stream* stream = file_dict->GetStreamFor("UF"); + CPDF_Dictionary* stream_dict = stream->GetDict(); + stream_dict->SetNewFor("Params"); + EXPECT_TRUE(file_spec.GetParamsDict()); + + // Add a parameter to the params dictionary. + CPDF_Dictionary* params_dict = stream_dict->GetDictFor("Params"); + params_dict->SetNewFor("Size", 6); + EXPECT_EQ(6, file_spec.GetParamsDict()->GetIntegerFor("Size")); + } +} -- cgit v1.2.3