diff --git a/Tests/LibPDF/CMakeLists.txt b/Tests/LibPDF/CMakeLists.txt index 536be7f6fd4..b466a25a827 100644 --- a/Tests/LibPDF/CMakeLists.txt +++ b/Tests/LibPDF/CMakeLists.txt @@ -8,6 +8,7 @@ endforeach() set(TEST_FILES complex.pdf + encryption_nocopy.pdf linearized.pdf non-linearized.pdf password-is-sup.pdf diff --git a/Tests/LibPDF/TestPDF.cpp b/Tests/LibPDF/TestPDF.cpp index 56483cdc8ba..d82762506fa 100644 --- a/Tests/LibPDF/TestPDF.cpp +++ b/Tests/LibPDF/TestPDF.cpp @@ -61,3 +61,15 @@ TEST_CASE(encrypted_with_aes) EXPECT_EQ(MUST(info_dict.title()).value(), "sup"); EXPECT_EQ(MUST(info_dict.creator()).value(), "TextEdit"); } + +TEST_CASE(encrypted_object_stream) +{ + auto file = MUST(Core::MappedFile::map("encryption_nocopy.pdf"sv)); + auto document = MUST(PDF::Document::create(file->bytes())); + MUST(document->initialize()); + EXPECT_EQ(document->get_page_count(), 1U); + + auto info_dict = MUST(document->info_dict()).value(); + EXPECT_EQ(MUST(info_dict.author()).value(), "van der Knijff"); + EXPECT_EQ(MUST(info_dict.creator()).value(), "Acrobat PDFMaker 9.1 voor Word"); +} diff --git a/Tests/LibPDF/encryption_nocopy.pdf b/Tests/LibPDF/encryption_nocopy.pdf new file mode 100644 index 00000000000..fe0efce40d6 Binary files /dev/null and b/Tests/LibPDF/encryption_nocopy.pdf differ diff --git a/Userland/Libraries/LibPDF/DocumentParser.cpp b/Userland/Libraries/LibPDF/DocumentParser.cpp index b608de763ef..3bf17f635ce 100644 --- a/Userland/Libraries/LibPDF/DocumentParser.cpp +++ b/Userland/Libraries/LibPDF/DocumentParser.cpp @@ -568,7 +568,9 @@ PDFErrorOr DocumentParser::parse_compressed_object_with_index(u32 index) if (m_reader.matches_eol()) m_reader.consume_eol(); + push_reference({ static_cast(first_number.get()), static_cast(second_number.get()) }); auto dict = TRY(parse_dict()); + auto type = TRY(dict->get_name(m_document, CommonNames::Type))->name(); if (type != "ObjStm") return error("Invalid object stream type"); @@ -577,8 +579,13 @@ PDFErrorOr DocumentParser::parse_compressed_object_with_index(u32 index) auto first_object_offset = dict->get_value("First").get_u32(); auto stream = TRY(parse_stream(dict)); + pop_reference(); + Parser stream_parser(m_document, stream->bytes()); + // The data was already decrypted when reading the outer compressed ObjStm. + stream_parser.set_encryption_enabled(false); + for (u32 i = 0; i < object_count; ++i) { auto object_number = TRY(stream_parser.parse_number()); auto object_offset = TRY(stream_parser.parse_number()); @@ -589,7 +596,11 @@ PDFErrorOr DocumentParser::parse_compressed_object_with_index(u32 index) } } - return TRY(stream_parser.parse_value()); + stream_parser.push_reference({ index, 0 }); + auto value = TRY(stream_parser.parse_value()); + stream_parser.pop_reference(); + + return value; } PDFErrorOr DocumentParser::parse_page_offset_hint_table(ReadonlyBytes hint_stream_bytes) diff --git a/Userland/Libraries/LibPDF/Parser.h b/Userland/Libraries/LibPDF/Parser.h index 64edd70e335..99ec82d8ecf 100644 --- a/Userland/Libraries/LibPDF/Parser.h +++ b/Userland/Libraries/LibPDF/Parser.h @@ -62,10 +62,15 @@ public: m_enable_filters = enabled; } -protected: + void set_encryption_enabled(bool enabled) + { + m_enable_encryption = enabled; + } + void push_reference(Reference const& ref) { m_current_reference_stack.append(ref); } void pop_reference() { m_current_reference_stack.take_last(); } +protected: Error error( DeprecatedString const& message #ifdef PDF_DEBUG