Add test for reading marked content with an indirect dict.

Bug: pdfium:1118
Change-Id: I2e9adaae64cba5b3915c19f3c99743cddbe58736
Reviewed-on: https://pdfium-review.googlesource.com/37371
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Henrique Nakashima <hnakashima@chromium.org>
diff --git a/fpdfsdk/fpdf_edit_embeddertest.cpp b/fpdfsdk/fpdf_edit_embeddertest.cpp
index a9eca92..be872ee 100644
--- a/fpdfsdk/fpdf_edit_embeddertest.cpp
+++ b/fpdfsdk/fpdf_edit_embeddertest.cpp
@@ -519,6 +519,117 @@
   FPDFPageObj_Destroy(page_object);
 }
 
+void CheckMarkCounts(FPDF_PAGE page,
+                     int start_from,
+                     int expected_object_count,
+                     size_t expected_prime_count,
+                     size_t expected_square_count,
+                     size_t expected_greater_than_ten_count,
+                     size_t expected_bounds_count) {
+  int object_count = FPDFPage_CountObjects(page);
+  ASSERT_EQ(expected_object_count, object_count);
+
+  size_t prime_count = 0;
+  size_t square_count = 0;
+  size_t greater_than_ten_count = 0;
+  size_t bounds_count = 0;
+  for (int i = 0; i < object_count; ++i) {
+    FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
+
+    int mark_count = FPDFPageObj_CountMarks(page_object);
+    for (int j = 0; j < mark_count; ++j) {
+      FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
+
+      char buffer[256];
+      ASSERT_GT(FPDFPageObjMark_GetName(mark, buffer, 256), 0u);
+      std::wstring name =
+          GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+      if (name == L"Prime") {
+        prime_count++;
+      } else if (name == L"Square") {
+        square_count++;
+        int expected_square = start_from + i;
+        EXPECT_EQ(1, FPDFPageObjMark_CountParams(mark));
+
+        unsigned long get_param_key_return =
+            FPDFPageObjMark_GetParamKey(mark, 0, buffer, 256);
+        ASSERT_GT(get_param_key_return, 0u);
+        EXPECT_EQ((6u + 1u) * 2u, get_param_key_return);
+        std::wstring key =
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+        EXPECT_EQ(L"Factor", key);
+
+        EXPECT_EQ(FPDF_OBJECT_NUMBER,
+                  FPDFPageObjMark_GetParamValueType(mark, 0));
+        int square_root = FPDFPageObjMark_GetParamIntValue(mark, 0);
+        EXPECT_EQ(expected_square, square_root * square_root);
+
+        EXPECT_EQ(FPDF_OBJECT_NUMBER,
+                  FPDFPageObjMark_GetParamValueTypeByKey(mark, "Factor"));
+        EXPECT_TRUE(FPDFPageObjMark_GetParamIntValueByKey(mark, "Factor",
+                                                          &square_root));
+        EXPECT_EQ(expected_square, square_root * square_root);
+      } else if (name == L"GreaterThanTen") {
+        greater_than_ten_count++;
+      } else if (name == L"Bounds") {
+        bounds_count++;
+        EXPECT_EQ(1, FPDFPageObjMark_CountParams(mark));
+
+        unsigned long get_param_key_return =
+            FPDFPageObjMark_GetParamKey(mark, 0, buffer, 256);
+        ASSERT_GT(get_param_key_return, 0u);
+        EXPECT_EQ((8u + 1u) * 2u, get_param_key_return);
+        std::wstring key =
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+        EXPECT_EQ(L"Position", key);
+
+        EXPECT_EQ(FPDF_OBJECT_STRING,
+                  FPDFPageObjMark_GetParamValueType(mark, 0));
+        unsigned long get_param_value_return =
+            FPDFPageObjMark_GetParamStringValue(mark, 0, buffer, 256);
+        ASSERT_GT(get_param_value_return, 0u);
+        EXPECT_EQ((4u + 1u) * 2u, get_param_value_return);
+        std::wstring value =
+            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+        EXPECT_EQ(L"Last", value);
+
+        // Should be the last object.
+        EXPECT_EQ(object_count - 1, i);
+
+        EXPECT_EQ(FPDF_OBJECT_STRING,
+                  FPDFPageObjMark_GetParamValueTypeByKey(mark, "Position"));
+        unsigned long length;
+        EXPECT_TRUE(FPDFPageObjMark_GetParamStringValueByKey(
+            mark, "Position", buffer, 256, &length));
+        ASSERT_GT(length, 0u);
+        EXPECT_EQ((4u + 1u) * 2u, length);
+        value = GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
+        EXPECT_EQ(L"Last", value);
+      } else {
+        FAIL();
+      }
+    }
+  }
+
+  // Expect certain number of tagged objects. The test file contains strings
+  // from 1 to 19.
+  EXPECT_EQ(expected_prime_count, prime_count);
+  EXPECT_EQ(expected_square_count, square_count);
+  EXPECT_EQ(expected_greater_than_ten_count, greater_than_ten_count);
+  EXPECT_EQ(expected_bounds_count, bounds_count);
+}
+
+TEST_F(FPDFEditEmbeddertest, ReadMarkedObjectsIndirectDict) {
+  // Load document with some text marked with an indirect property.
+  EXPECT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
+  FPDF_PAGE page = LoadPage(0);
+  ASSERT_TRUE(page);
+
+  CheckMarkCounts(page, 1, 19, 8, 4, 9, 1);
+
+  UnloadPage(page);
+}
+
 TEST_F(FPDFEditEmbeddertest, RemoveMarkedObjectsPrime) {
   // Load document with some text.
   EXPECT_TRUE(OpenDocument("text_in_page_marked.pdf"));
@@ -538,16 +649,12 @@
     CompareBitmap(page_bitmap.get(), 200, 200, kOriginalMD5);
   }
 
-  // Iterate over all objects, counting the number of times each content mark
-  // name appears.
-  int object_count = FPDFPage_CountObjects(page);
-  ASSERT_EQ(19, object_count);
+  constexpr int expected_object_count = 19;
+  CheckMarkCounts(page, 1, expected_object_count, 8, 4, 9, 1);
 
-  unsigned long prime_count = 0;
-  unsigned long square_count = 0;
-  unsigned long greater_than_ten_count = 0;
+  // Get all objects marked with "Prime"
   std::vector<FPDF_PAGEOBJECT> primes;
-  for (int i = 0; i < object_count; ++i) {
+  for (int i = 0; i < expected_object_count; ++i) {
     FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
 
     int mark_count = FPDFPageObj_CountMarks(page_object);
@@ -559,64 +666,11 @@
       std::wstring name =
           GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
       if (name == L"Prime") {
-        prime_count++;
-        EXPECT_EQ(0, FPDFPageObjMark_CountParams(mark));
         primes.push_back(page_object);
-      } else if (name == L"Square") {
-        square_count++;
-        EXPECT_EQ(1, FPDFPageObjMark_CountParams(mark));
-        ASSERT_GT(FPDFPageObjMark_GetParamKey(mark, 0, buffer, 256), 0u);
-        std::wstring key =
-            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
-        EXPECT_EQ(L"Factor", key);
-        EXPECT_EQ(FPDF_OBJECT_NUMBER,
-                  FPDFPageObjMark_GetParamValueType(mark, 0));
-        int square_root = FPDFPageObjMark_GetParamIntValue(mark, 0);
-        EXPECT_EQ(i + 1, square_root * square_root);
-
-        EXPECT_EQ(FPDF_OBJECT_NUMBER,
-                  FPDFPageObjMark_GetParamValueTypeByKey(mark, "Factor"));
-        EXPECT_TRUE(FPDFPageObjMark_GetParamIntValueByKey(mark, "Factor",
-                                                          &square_root));
-        EXPECT_EQ(i + 1, square_root * square_root);
-      } else if (name == L"GreaterThanTen") {
-        greater_than_ten_count++;
-        EXPECT_EQ(0, FPDFPageObjMark_CountParams(mark));
-      } else if (name == L"Bounds") {
-        EXPECT_EQ(1, FPDFPageObjMark_CountParams(mark));
-        ASSERT_GT(FPDFPageObjMark_GetParamKey(mark, 0, buffer, 256), 0u);
-        std::wstring key =
-            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
-        EXPECT_EQ(L"Position", key);
-        EXPECT_EQ(FPDF_OBJECT_STRING,
-                  FPDFPageObjMark_GetParamValueType(mark, 0));
-        ASSERT_GT(FPDFPageObjMark_GetParamStringValue(mark, 0, buffer, 256),
-                  0u);
-        std::wstring value =
-            GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
-        EXPECT_EQ(L"Last", value);
-        EXPECT_EQ(18, i);
-
-        EXPECT_EQ(FPDF_OBJECT_STRING,
-                  FPDFPageObjMark_GetParamValueTypeByKey(mark, "Position"));
-        unsigned long length;
-        EXPECT_TRUE(FPDFPageObjMark_GetParamStringValueByKey(
-            mark, "Position", buffer, 256, &length));
-        ASSERT_GT(length, 0u);
-        value = GetPlatformWString(reinterpret_cast<unsigned short*>(buffer));
-        EXPECT_EQ(L"Last", value);
-      } else {
-        FAIL();
       }
     }
   }
 
-  // Expect certain number of tagged objects. The test file contains strings
-  // from 1 to 19.
-  EXPECT_EQ(8u, prime_count);
-  EXPECT_EQ(4u, square_count);
-  EXPECT_EQ(9u, greater_than_ten_count);
-
   // Remove all objects marked with "Prime".
   for (FPDF_PAGEOBJECT page_object : primes) {
     EXPECT_TRUE(FPDFPage_RemoveObject(page, page_object));
diff --git a/testing/resources/text_in_page_marked_indirect.in b/testing/resources/text_in_page_marked_indirect.in
new file mode 100644
index 0000000..1f989f6
--- /dev/null
+++ b/testing/resources/text_in_page_marked_indirect.in
@@ -0,0 +1,144 @@
+{{header}}
+{{object 1 0}}
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+{{object 2 0}}
+<< /Count 1 /Kids [ 3 0 R ] /MediaBox [ 0 0 200 200 ] /Type /Pages >>
+endobj
+{{object 3 0}}
+<<
+  /Contents 4 0 R
+  /Parent 2 0 R
+  /Resources <<
+    /ExtGState << /FXE1 5 0 R /FXE2 6 0 R >>
+    /Font << /F1 7 0 R /F2 8 0 R /FXF1 9 0 R /FXF2 10 0 R >>
+    /Properties <<
+      /LastBoundsProperty 13 0 R
+    >>
+  >>
+  /Type /Page
+>>
+endobj
+{{object 4 0}}
+[ 12 0 R ]
+endobj
+{{object 5 0}}
+<< /BM /Normal /CA 1 /ca 1 >>
+endobj
+{{object 6 0}}
+<< /ca 0.705882 >>
+endobj
+{{object 7 0}}
+<< /BaseFont /Times-Roman /Subtype /Type1 /Type /Font >>
+endobj
+{{object 8 0}}
+<< /BaseFont /Helvetica /Subtype /Type1 /Type /Font >>
+endobj
+{{object 9 0}}
+<< /BaseFont /Courier-Bold /Subtype /Type1 /Type /Font >>
+endobj
+{{object 10 0}}
+<< /BaseFont /Times-Bold /Subtype /Type1 /Type /Font >>
+endobj
+{{object 12 0}}
+<< {{streamlen}} >>
+stream
+q
+0 0 0 RG 0 0 0 rg 1 w 0 J 0 j
+/FXE1 gs
+/Square <</Factor 1>> BDC
+q 0 0 0 rg /FXE2 gs BT 1 0 0 1 120 100 Tm /FXF1 9 Tf (Test 1) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.0509804 rg /FXE2 gs
+BT 0.995597 -0.341789 0.341789 0.995597 119.912 93.1642
+Tm /FXF2 9 Tf (Test 2) Tj ET Q
+q 0 0 0.101961 rg /FXE2 gs
+BT 0.872208 -0.678867 0.678867 0.872208 117.444 86.4227
+Tm /FXF1 9 Tf (Test 3) Tj ET Q
+EMC
+/Square <</Factor 2>> BDC
+q 0 0 0.156863 rg /FXE2 gs
+BT 0.633308 -0.969351 0.969351 0.633308 112.666 80.613
+Tm /FXF2 9 Tf (Test 4) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.207843 rg /FXE2 gs
+BT 0.297167 -1.17348 1.17348 0.297167 105.943 76.5303
+Tm /FXF1 9 Tf (Test 5) Tj ET Q
+EMC
+q 0 0 0.262745 rg /FXE2 gs
+BT -0.104311 -1.25884 1.25884 -0.104311 97.9138 74.8231
+Tm /FXF2 9 Tf (Test 6) Tj ET Q
+/Prime BMC
+q 0 0 0.313726 rg /FXE2 gs
+BT -0.528547 -1.20496 1.20496 -0.528547 89.4291 75.9007
+Tm /FXF1 9 Tf (Test 7) Tj ET Q
+EMC
+q 0 0 0.364706 rg /FXE2 gs
+BT -0.926806 -1.00678 1.00678 -0.926806 81.4639 79.8644
+Tm /FXF2 9 Tf (Test 8) Tj ET Q
+/Square <</Factor 3>> BDC
+q 0 0 0.419608 rg /FXE2 gs
+BT -1.24978 -0.676346 0.676346 -1.24978 75.0044 86.4731
+Tm /FXF1 9 Tf (Test 9) Tj ET Q
+EMC
+q 0 0 0.470588 rg /FXE2 gs
+BT -1.45359 -0.24256 0.24256 -1.45359 70.9283 95.1488
+Tm /FXF2 9 Tf (Test 10) Tj ET Q
+/GreaterThanTen BMC
+/Prime BMC
+q 0 0 0.52549 rg /FXE2 gs
+BT -1.5055 0.251223 -0.251223 -1.5055 69.89 105.024
+Tm /FXF1 9 Tf (Test 11) Tj ET Q
+EMC
+q 0 0 0.576471 rg /FXE2 gs
+BT -1.38864 0.751496 -0.751496 -1.38864 72.2271 115.03
+Tm /FXF2 9 Tf (Test 12) Tj ET Q
+/Prime BMC
+q 0 0 0.631373 rg /FXE2 gs
+BT -1.10504 1.20039 -1.20039 -1.10504 77.8992 124.008
+Tm /FXF1 9 Tf (Test 13) Tj ET Q
+EMC
+q 0 0 0.682353 rg /FXE2 gs
+BT -0.67654 1.54236 -1.54236 -0.67654 86.4692 130.847
+Tm /FXF2 9 Tf (Test 14) Tj ET Q
+q 0 0 0.733333 rg /FXE2 gs
+BT -0.143427 1.73091 -1.73091 -0.143427 97.1315 134.618
+Tm /FXF1 9 Tf (Test 15) Tj ET Q
+/Square <</Factor 4>> BDC
+q 0 0 0.788235 rg /FXE2 gs
+BT 0.43929 1.73472 -1.73472 0.43929 108.786 134.694
+Tm /FXF2 9 Tf (Test 16) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.839216 rg /FXE2 gs
+BT 1.00754 1.54215 -1.54215 1.00754 120.151 130.843
+Tm /FXF1 9 Tf (Test 17) Tj ET Q
+EMC
+q 0 0 0.894118 rg /FXE2 gs
+BT 1.49521 1.16377 -1.16377 1.49521 129.904 123.275
+Tm /FXF2 9 Tf (Test 18) Tj ET Q
+/Bounds /LastBoundsProperty BDC
+/Prime BMC
+q 0 0 0.945098 rg /FXE2 gs
+BT 1.84185 0.632309 -0.632309 1.84185 136.837 112.646
+Tm /FXF1 9 Tf (Test 19) Tj ET Q
+EMC
+EMC
+EMC
+Q
+endstream
+endobj
+{{object 13 0}}
+<< /Position (Last) >>
+endobj
+
+{{xref}}
+trailer <<
+  /Root 1 0 R
+  /Size 14
+  /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/text_in_page_marked_indirect.pdf b/testing/resources/text_in_page_marked_indirect.pdf
new file mode 100644
index 0000000..8756291
--- /dev/null
+++ b/testing/resources/text_in_page_marked_indirect.pdf
@@ -0,0 +1,161 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj
+<< /Pages 2 0 R /Type /Catalog >>
+endobj
+2 0 obj
+<< /Count 1 /Kids [ 3 0 R ] /MediaBox [ 0 0 200 200 ] /Type /Pages >>
+endobj
+3 0 obj
+<<
+  /Contents 4 0 R
+  /Parent 2 0 R
+  /Resources <<
+    /ExtGState << /FXE1 5 0 R /FXE2 6 0 R >>
+    /Font << /F1 7 0 R /F2 8 0 R /FXF1 9 0 R /FXF2 10 0 R >>
+    /Properties <<
+      /LastBoundsProperty 13 0 R
+    >>
+  >>
+  /Type /Page
+>>
+endobj
+4 0 obj
+[ 12 0 R ]
+endobj
+5 0 obj
+<< /BM /Normal /CA 1 /ca 1 >>
+endobj
+6 0 obj
+<< /ca 0.705882 >>
+endobj
+7 0 obj
+<< /BaseFont /Times-Roman /Subtype /Type1 /Type /Font >>
+endobj
+8 0 obj
+<< /BaseFont /Helvetica /Subtype /Type1 /Type /Font >>
+endobj
+9 0 obj
+<< /BaseFont /Courier-Bold /Subtype /Type1 /Type /Font >>
+endobj
+10 0 obj
+<< /BaseFont /Times-Bold /Subtype /Type1 /Type /Font >>
+endobj
+12 0 obj
+<< /Length 2432 >>
+stream
+q
+0 0 0 RG 0 0 0 rg 1 w 0 J 0 j
+/FXE1 gs
+/Square <</Factor 1>> BDC
+q 0 0 0 rg /FXE2 gs BT 1 0 0 1 120 100 Tm /FXF1 9 Tf (Test 1) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.0509804 rg /FXE2 gs
+BT 0.995597 -0.341789 0.341789 0.995597 119.912 93.1642
+Tm /FXF2 9 Tf (Test 2) Tj ET Q
+q 0 0 0.101961 rg /FXE2 gs
+BT 0.872208 -0.678867 0.678867 0.872208 117.444 86.4227
+Tm /FXF1 9 Tf (Test 3) Tj ET Q
+EMC
+/Square <</Factor 2>> BDC
+q 0 0 0.156863 rg /FXE2 gs
+BT 0.633308 -0.969351 0.969351 0.633308 112.666 80.613
+Tm /FXF2 9 Tf (Test 4) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.207843 rg /FXE2 gs
+BT 0.297167 -1.17348 1.17348 0.297167 105.943 76.5303
+Tm /FXF1 9 Tf (Test 5) Tj ET Q
+EMC
+q 0 0 0.262745 rg /FXE2 gs
+BT -0.104311 -1.25884 1.25884 -0.104311 97.9138 74.8231
+Tm /FXF2 9 Tf (Test 6) Tj ET Q
+/Prime BMC
+q 0 0 0.313726 rg /FXE2 gs
+BT -0.528547 -1.20496 1.20496 -0.528547 89.4291 75.9007
+Tm /FXF1 9 Tf (Test 7) Tj ET Q
+EMC
+q 0 0 0.364706 rg /FXE2 gs
+BT -0.926806 -1.00678 1.00678 -0.926806 81.4639 79.8644
+Tm /FXF2 9 Tf (Test 8) Tj ET Q
+/Square <</Factor 3>> BDC
+q 0 0 0.419608 rg /FXE2 gs
+BT -1.24978 -0.676346 0.676346 -1.24978 75.0044 86.4731
+Tm /FXF1 9 Tf (Test 9) Tj ET Q
+EMC
+q 0 0 0.470588 rg /FXE2 gs
+BT -1.45359 -0.24256 0.24256 -1.45359 70.9283 95.1488
+Tm /FXF2 9 Tf (Test 10) Tj ET Q
+/GreaterThanTen BMC
+/Prime BMC
+q 0 0 0.52549 rg /FXE2 gs
+BT -1.5055 0.251223 -0.251223 -1.5055 69.89 105.024
+Tm /FXF1 9 Tf (Test 11) Tj ET Q
+EMC
+q 0 0 0.576471 rg /FXE2 gs
+BT -1.38864 0.751496 -0.751496 -1.38864 72.2271 115.03
+Tm /FXF2 9 Tf (Test 12) Tj ET Q
+/Prime BMC
+q 0 0 0.631373 rg /FXE2 gs
+BT -1.10504 1.20039 -1.20039 -1.10504 77.8992 124.008
+Tm /FXF1 9 Tf (Test 13) Tj ET Q
+EMC
+q 0 0 0.682353 rg /FXE2 gs
+BT -0.67654 1.54236 -1.54236 -0.67654 86.4692 130.847
+Tm /FXF2 9 Tf (Test 14) Tj ET Q
+q 0 0 0.733333 rg /FXE2 gs
+BT -0.143427 1.73091 -1.73091 -0.143427 97.1315 134.618
+Tm /FXF1 9 Tf (Test 15) Tj ET Q
+/Square <</Factor 4>> BDC
+q 0 0 0.788235 rg /FXE2 gs
+BT 0.43929 1.73472 -1.73472 0.43929 108.786 134.694
+Tm /FXF2 9 Tf (Test 16) Tj ET Q
+EMC
+/Prime BMC
+q 0 0 0.839216 rg /FXE2 gs
+BT 1.00754 1.54215 -1.54215 1.00754 120.151 130.843
+Tm /FXF1 9 Tf (Test 17) Tj ET Q
+EMC
+q 0 0 0.894118 rg /FXE2 gs
+BT 1.49521 1.16377 -1.16377 1.49521 129.904 123.275
+Tm /FXF2 9 Tf (Test 18) Tj ET Q
+/Bounds /LastBoundsProperty BDC
+/Prime BMC
+q 0 0 0.945098 rg /FXE2 gs
+BT 1.84185 0.632309 -0.632309 1.84185 136.837 112.646
+Tm /FXF1 9 Tf (Test 19) Tj ET Q
+EMC
+EMC
+EMC
+Q
+endstream
+endobj
+13 0 obj
+<< /Position (Last) >>
+endobj
+
+xref
+0 14
+0000000000 65535 f 
+0000000015 00000 n 
+0000000064 00000 n 
+0000000149 00000 n 
+0000000404 00000 n 
+0000000430 00000 n 
+0000000475 00000 n 
+0000000509 00000 n 
+0000000581 00000 n 
+0000000651 00000 n 
+0000000724 00000 n 
+0000000000 65535 f 
+0000000796 00000 n 
+0000003280 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 14
+  /ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
+>>
+startxref
+3320
+%%EOF