Ensure CPDF_Stream's dictionary is always a direct object

Make CPDF_Stream behave closer to the PDF spec. Add a deliberately bad
pixel test, which is similar to hello_world.pdf, to verify the existing
parser code already handles this case correctly.

Bug: pdfium:2119
Change-Id: Iabd791c2b3b8fa31433fb257f580868c06e16922
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/115693
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Thomas Sepez <tsepez@google.com>
diff --git a/core/fpdfapi/parser/cpdf_stream.cpp b/core/fpdfapi/parser/cpdf_stream.cpp
index d1fa557..0cb4b1e 100644
--- a/core/fpdfapi/parser/cpdf_stream.cpp
+++ b/core/fpdfapi/parser/cpdf_stream.cpp
@@ -53,7 +53,7 @@
 CPDF_Stream::CPDF_Stream(RetainPtr<IFX_SeekableReadStream> file,
                          RetainPtr<CPDF_Dictionary> dict)
     : data_(std::move(file)), dict_(std::move(dict)) {
-  CHECK(dict_);
+  CHECK(dict_->IsInline());
   SetLengthInDict(pdfium::base::checked_cast<int>(
       absl::get<RetainPtr<IFX_SeekableReadStream>>(data_)->GetSize()));
 }
@@ -61,7 +61,7 @@
 CPDF_Stream::CPDF_Stream(DataVector<uint8_t> data,
                          RetainPtr<CPDF_Dictionary> dict)
     : data_(std::move(data)), dict_(std::move(dict)) {
-  CHECK(dict_);
+  CHECK(dict_->IsInline());
   SetLengthInDict(pdfium::base::checked_cast<int>(
       absl::get<DataVector<uint8_t>>(data_).size()));
 }
diff --git a/core/fpdfapi/parser/cpdf_stream.h b/core/fpdfapi/parser/cpdf_stream.h
index 42f5683..2311bf2 100644
--- a/core/fpdfapi/parser/cpdf_stream.h
+++ b/core/fpdfapi/parser/cpdf_stream.h
@@ -68,7 +68,7 @@
   friend class CPDF_Dictionary;
 
   // Initializes with empty data and /Length set to 0 in `dict`.
-  // `dict` must be non-null.
+  // `dict` must be non-null and be a direct object.
   explicit CPDF_Stream(RetainPtr<CPDF_Dictionary> dict);
 
   // Copies `span` and `stream`, respectively. Creates a new dictionary with the
@@ -77,12 +77,12 @@
   explicit CPDF_Stream(fxcrt::ostringstream* stream);
 
   // Reads data from `file`. `dict` will have its /Length set based on `file`.
-  // `dict` must be non-null.
+  // `dict` must be non-null and be a direct object.
   CPDF_Stream(RetainPtr<IFX_SeekableReadStream> file,
               RetainPtr<CPDF_Dictionary> dict);
 
   // Takes `data`.
-  // `dict` must be non-null.
+  // `dict` must be non-null and be a direct object.
   CPDF_Stream(DataVector<uint8_t> data, RetainPtr<CPDF_Dictionary> dict);
   ~CPDF_Stream() override;
 
diff --git a/testing/resources/pixel/stream_with_indirect_dict.in b/testing/resources/pixel/stream_with_indirect_dict.in
new file mode 100644
index 0000000..d882577
--- /dev/null
+++ b/testing/resources/pixel/stream_with_indirect_dict.in
@@ -0,0 +1,57 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 200]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <<
+      /F1 4 0 R
+      /F2 5 0 R
+    >>
+  >>
+  /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} 7 0 R
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+  /Length 83
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/stream_with_indirect_dict_expected.pdf.0.png b/testing/resources/pixel/stream_with_indirect_dict_expected.pdf.0.png
new file mode 100644
index 0000000..f97e340
--- /dev/null
+++ b/testing/resources/pixel/stream_with_indirect_dict_expected.pdf.0.png
Binary files differ