| // Copyright 2021 The PDFium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "core/fpdfapi/parser/cpdf_object_stream.h" | |
| #include <utility> | |
| #include "core/fpdfapi/parser/cpdf_dictionary.h" | |
| #include "core/fpdfapi/parser/cpdf_indirect_object_holder.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/fxcrt/data_vector.h" | |
| #include "testing/gmock/include/gmock/gmock.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| using testing::ElementsAre; | |
| namespace { | |
| constexpr char kNormalStreamContent[] = | |
| "10 0 11 14 12 21<</Name /Foo>>[1 2 3]4"; | |
| constexpr int kNormalStreamContentOffset = 16; | |
| static_assert(kNormalStreamContent[kNormalStreamContentOffset] == '<', | |
| "Wrong offset"); | |
| static_assert(kNormalStreamContent[kNormalStreamContentOffset + 1] == '<', | |
| "Wrong offset"); | |
| } // namespace | |
| TEST(ObjectStreamTest, StreamDictNormal) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", kNormalStreamContentOffset); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), | |
| CPDF_ObjectStream::ObjectInfo(11, 14), | |
| CPDF_ObjectStream::ObjectInfo(12, 21))); | |
| // Check expected indices. | |
| CPDF_IndirectObjectHolder holder; | |
| RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 0); | |
| ASSERT_TRUE(obj10); | |
| EXPECT_EQ(10u, obj10->GetObjNum()); | |
| EXPECT_EQ(0u, obj10->GetGenNum()); | |
| EXPECT_TRUE(obj10->IsDictionary()); | |
| RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11, 1); | |
| ASSERT_TRUE(obj11); | |
| EXPECT_EQ(11u, obj11->GetObjNum()); | |
| EXPECT_EQ(0u, obj11->GetGenNum()); | |
| EXPECT_TRUE(obj11->IsArray()); | |
| RetainPtr<CPDF_Object> obj12 = obj_stream->ParseObject(&holder, 12, 2); | |
| ASSERT_TRUE(obj12); | |
| EXPECT_EQ(12u, obj12->GetObjNum()); | |
| EXPECT_EQ(0u, obj12->GetGenNum()); | |
| EXPECT_TRUE(obj12->IsNumber()); | |
| // Check bad indices. | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 1)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 2)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 3)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 0)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 2)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 3)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 0)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 1)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 3)); | |
| } | |
| TEST(ObjectStreamTest, StreamEmptyDict) { | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = | |
| pdfium::MakeRetain<CPDF_Stream>(ToDataVector<uint8_t>(contents_view), | |
| pdfium::MakeRetain<CPDF_Dictionary>()); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictNoType) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 5); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictWrongType) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_String>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 5); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictWrongTypeValue) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStmmmm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 5); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictNoCount) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("First", 5); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictFloatCount) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 2.2f); | |
| dict->SetNewFor<CPDF_Number>("First", 5); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictNegativeCount) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", -1); | |
| dict->SetNewFor<CPDF_Number>("First", 5); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictCountTooBig) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 999999999); | |
| dict->SetNewFor<CPDF_Number>("First", 5); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictNoOffset) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictFloatOffset) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 5.5f); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictNegativeOffset) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", -5); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| EXPECT_FALSE(CPDF_ObjectStream::Create(std::move(stream))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictOffsetTooBig) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| static constexpr int kTooBigOffset = std::size(kNormalStreamContent); | |
| dict->SetNewFor<CPDF_Number>("First", kTooBigOffset); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), | |
| CPDF_ObjectStream::ObjectInfo(11, 14), | |
| CPDF_ObjectStream::ObjectInfo(12, 21))); | |
| CPDF_IndirectObjectHolder holder; | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 0)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 1)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 2)); | |
| } | |
| TEST(ObjectStreamTest, StreamDictTooFewCount) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 2); | |
| dict->SetNewFor<CPDF_Number>("First", kNormalStreamContentOffset); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), | |
| CPDF_ObjectStream::ObjectInfo(11, 14))); | |
| CPDF_IndirectObjectHolder holder; | |
| RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 0); | |
| ASSERT_TRUE(obj10); | |
| EXPECT_EQ(10u, obj10->GetObjNum()); | |
| EXPECT_EQ(0u, obj10->GetGenNum()); | |
| EXPECT_TRUE(obj10->IsDictionary()); | |
| RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11, 1); | |
| ASSERT_TRUE(obj11); | |
| EXPECT_EQ(11u, obj11->GetObjNum()); | |
| EXPECT_EQ(0u, obj11->GetGenNum()); | |
| EXPECT_TRUE(obj11->IsArray()); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 12, 2)); | |
| } | |
| TEST(ObjectStreamTest, StreamDictTooManyObject) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 9); | |
| dict->SetNewFor<CPDF_Number>("First", kNormalStreamContentOffset); | |
| ByteStringView contents_view(kNormalStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| // TODO(thestig): Can this avoid finding object 2? | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), | |
| CPDF_ObjectStream::ObjectInfo(11, 14), | |
| CPDF_ObjectStream::ObjectInfo(12, 21), | |
| CPDF_ObjectStream::ObjectInfo(2, 3))); | |
| CPDF_IndirectObjectHolder holder; | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 0)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 1)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 2)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 3)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 2, 4)); | |
| } | |
| TEST(ObjectStreamTest, StreamDictGarbageObjNum) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 19); | |
| const char kStreamContent[] = "10 0 hi 14 12 21<</Name /Foo>>[1 2 3]4"; | |
| ByteStringView contents_view(kStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), | |
| CPDF_ObjectStream::ObjectInfo(12, 21))); | |
| } | |
| TEST(ObjectStreamTest, StreamDictGarbageObjectOffset) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 16); | |
| const char kStreamContent[] = "10 0 11 hi 12 21<</Name /Foo>>[1 2 3]4"; | |
| ByteStringView contents_view(kStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| // TODO(thestig): Should object 11 be rejected? | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), | |
| CPDF_ObjectStream::ObjectInfo(11, 0), | |
| CPDF_ObjectStream::ObjectInfo(12, 21))); | |
| CPDF_IndirectObjectHolder holder; | |
| RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 0); | |
| ASSERT_TRUE(obj10); | |
| EXPECT_EQ(10u, obj10->GetObjNum()); | |
| EXPECT_EQ(0u, obj10->GetGenNum()); | |
| EXPECT_TRUE(obj10->IsDictionary()); | |
| RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11, 1); | |
| ASSERT_TRUE(obj11); | |
| EXPECT_EQ(11u, obj11->GetObjNum()); | |
| EXPECT_EQ(0u, obj11->GetGenNum()); | |
| EXPECT_TRUE(obj11->IsDictionary()); | |
| } | |
| TEST(ObjectStreamTest, StreamDictNegativeObjectOffset) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 16); | |
| const char kStreamContent[] = "10 0 11 -1 12 21<</Name /Foo>>[1 2 3]4"; | |
| ByteStringView contents_view(kStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| // TODO(thestig): Should object 11 be rejected? | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), | |
| CPDF_ObjectStream::ObjectInfo(11, 4294967295), | |
| CPDF_ObjectStream::ObjectInfo(12, 21))); | |
| CPDF_IndirectObjectHolder holder; | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 1)); | |
| } | |
| TEST(ObjectStreamTest, StreamDictObjectOffsetTooBig) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 17); | |
| const char kStreamContent[] = "10 0 11 999 12 21<</Name /Foo>>[1 2 3]4"; | |
| ByteStringView contents_view(kStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| // TODO(thestig): Should object 11 be rejected? | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), | |
| CPDF_ObjectStream::ObjectInfo(11, 999), | |
| CPDF_ObjectStream::ObjectInfo(12, 21))); | |
| CPDF_IndirectObjectHolder holder; | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 11, 1)); | |
| } | |
| TEST(ObjectStreamTest, StreamDictDuplicateObjNum) { | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 16); | |
| const char kStreamContent[] = "10 0 10 14 12 21<</Name /Foo>>[1 2 3]4"; | |
| ByteStringView contents_view(kStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 0), | |
| CPDF_ObjectStream::ObjectInfo(10, 14), | |
| CPDF_ObjectStream::ObjectInfo(12, 21))); | |
| CPDF_IndirectObjectHolder holder; | |
| RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 0); | |
| ASSERT_TRUE(obj10); | |
| EXPECT_EQ(10u, obj10->GetObjNum()); | |
| EXPECT_EQ(0u, obj10->GetGenNum()); | |
| EXPECT_TRUE(obj10->IsDictionary()); | |
| obj10 = obj_stream->ParseObject(&holder, 10, 1); | |
| ASSERT_TRUE(obj10); | |
| EXPECT_EQ(10u, obj10->GetObjNum()); | |
| EXPECT_EQ(0u, obj10->GetGenNum()); | |
| EXPECT_TRUE(obj10->IsArray()); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 2)); | |
| EXPECT_FALSE(obj_stream->ParseObject(&holder, 10, 3)); | |
| RetainPtr<CPDF_Object> obj12 = obj_stream->ParseObject(&holder, 12, 2); | |
| ASSERT_TRUE(obj12); | |
| EXPECT_EQ(12u, obj12->GetObjNum()); | |
| EXPECT_EQ(0u, obj12->GetGenNum()); | |
| EXPECT_TRUE(obj12->IsNumber()); | |
| } | |
| TEST(ObjectStreamTest, StreamDictUnorderedObjectNumbers) { | |
| // ISO 32000-1:2008 spec. section 7.5.7, note 6 says there is no restriction | |
| // on object number ordering. | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 16); | |
| const char kStreamContent[] = "11 0 12 14 10 21<</Name /Foo>>[1 2 3]4"; | |
| ByteStringView contents_view(kStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(11, 0), | |
| CPDF_ObjectStream::ObjectInfo(12, 14), | |
| CPDF_ObjectStream::ObjectInfo(10, 21))); | |
| CPDF_IndirectObjectHolder holder; | |
| RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 2); | |
| ASSERT_TRUE(obj10); | |
| EXPECT_EQ(10u, obj10->GetObjNum()); | |
| EXPECT_EQ(0u, obj10->GetGenNum()); | |
| EXPECT_TRUE(obj10->IsNumber()); | |
| RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11, 0); | |
| ASSERT_TRUE(obj11); | |
| EXPECT_EQ(11u, obj11->GetObjNum()); | |
| EXPECT_EQ(0u, obj11->GetGenNum()); | |
| EXPECT_TRUE(obj11->IsDictionary()); | |
| RetainPtr<CPDF_Object> obj12 = obj_stream->ParseObject(&holder, 12, 1); | |
| ASSERT_TRUE(obj12); | |
| EXPECT_EQ(12u, obj12->GetObjNum()); | |
| EXPECT_EQ(0u, obj12->GetGenNum()); | |
| EXPECT_TRUE(obj12->IsArray()); | |
| } | |
| TEST(ObjectStreamTest, StreamDictUnorderedObjectOffsets) { | |
| // ISO 32000-1:2008 spec. section 7.5.7, says offsets shall be in increasing | |
| // order. | |
| // TODO(thestig): Should CPDF_ObjectStream check for this and reject this | |
| // object stream? | |
| auto dict = pdfium::MakeRetain<CPDF_Dictionary>(); | |
| dict->SetNewFor<CPDF_Name>("Type", "ObjStm"); | |
| dict->SetNewFor<CPDF_Number>("N", 3); | |
| dict->SetNewFor<CPDF_Number>("First", 16); | |
| const char kStreamContent[] = "10 21 11 0 12 14<</Name /Foo>>[1 2 3]4"; | |
| ByteStringView contents_view(kStreamContent); | |
| auto stream = pdfium::MakeRetain<CPDF_Stream>( | |
| ToDataVector<uint8_t>(contents_view), dict); | |
| auto obj_stream = CPDF_ObjectStream::Create(std::move(stream)); | |
| ASSERT_TRUE(obj_stream); | |
| EXPECT_THAT(obj_stream->object_info(), | |
| ElementsAre(CPDF_ObjectStream::ObjectInfo(10, 21), | |
| CPDF_ObjectStream::ObjectInfo(11, 0), | |
| CPDF_ObjectStream::ObjectInfo(12, 14))); | |
| CPDF_IndirectObjectHolder holder; | |
| RetainPtr<CPDF_Object> obj10 = obj_stream->ParseObject(&holder, 10, 0); | |
| ASSERT_TRUE(obj10); | |
| EXPECT_EQ(10u, obj10->GetObjNum()); | |
| EXPECT_EQ(0u, obj10->GetGenNum()); | |
| EXPECT_TRUE(obj10->IsNumber()); | |
| RetainPtr<CPDF_Object> obj11 = obj_stream->ParseObject(&holder, 11, 1); | |
| ASSERT_TRUE(obj11); | |
| EXPECT_EQ(11u, obj11->GetObjNum()); | |
| EXPECT_EQ(0u, obj11->GetGenNum()); | |
| EXPECT_TRUE(obj11->IsDictionary()); | |
| RetainPtr<CPDF_Object> obj12 = obj_stream->ParseObject(&holder, 12, 2); | |
| ASSERT_TRUE(obj12); | |
| EXPECT_EQ(12u, obj12->GetObjNum()); | |
| EXPECT_EQ(0u, obj12->GetGenNum()); | |
| EXPECT_TRUE(obj12->IsArray()); | |
| } |