Provide correct state to pattern rendering. (try 2)

According to 8.7.3.1 in the PDF 2.0 spec, a tiling pattern should
install the graphics state that was in effect at the beginning of the
pattern’s parent content stream.

The first attempt in commit f188aca283d6dceb874bbe5e20d8003e6bbe184f got
reverted because of an ASAN fuzzer issue.

Those issues have been fixed by not caching the `CPDF_Form` in the
`CPDF_TilingPattern` and simply returning it in a `std::unique_ptr`.

In addition to that the regressions found in bug 1288 have also
been fixed and relevant test cases have been added.

Bug: pdfium:1286
Change-Id: I013dd59587caa9be32963c51934f5030ef1e72fb
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/53831
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_tilingpattern.cpp b/core/fpdfapi/page/cpdf_tilingpattern.cpp
index faa7eb8..8c30cdc 100644
--- a/core/fpdfapi/page/cpdf_tilingpattern.cpp
+++ b/core/fpdfapi/page/cpdf_tilingpattern.cpp
@@ -6,7 +6,9 @@
 
 #include "core/fpdfapi/page/cpdf_tilingpattern.h"
 
+#include "core/fpdfapi/page/cpdf_allstates.h"
 #include "core/fpdfapi/page/cpdf_form.h"
+#include "core/fpdfapi/page/cpdf_pageobject.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
@@ -31,13 +33,10 @@
   return nullptr;
 }
 
-bool CPDF_TilingPattern::Load() {
-  if (m_pForm)
-    return true;
-
+std::unique_ptr<CPDF_Form> CPDF_TilingPattern::Load(CPDF_PageObject* pPageObj) {
   const CPDF_Dictionary* pDict = pattern_obj()->GetDict();
   if (!pDict)
-    return false;
+    return nullptr;
 
   m_bColored = pDict->GetIntegerFor("PaintType") == 1;
   m_XStep = static_cast<float>(fabs(pDict->GetNumberFor("XStep")));
@@ -45,11 +44,16 @@
 
   CPDF_Stream* pStream = pattern_obj()->AsStream();
   if (!pStream)
-    return false;
+    return nullptr;
 
   const CFX_Matrix& matrix = parent_matrix();
-  m_pForm = pdfium::MakeUnique<CPDF_Form>(document(), nullptr, pStream);
-  m_pForm->ParseContent(nullptr, &matrix, nullptr, nullptr);
+  auto form = pdfium::MakeUnique<CPDF_Form>(document(), nullptr, pStream);
+
+  CPDF_AllStates allStates;
+  allStates.m_ColorState.Emplace();
+  allStates.m_GraphState.Emplace();
+  allStates.m_GeneralState = pPageObj->m_GeneralState;
+  form->ParseContent(&allStates, &matrix, nullptr, nullptr);
   m_BBox = pDict->GetRectFor("BBox");
-  return true;
+  return form;
 }
diff --git a/core/fpdfapi/page/cpdf_tilingpattern.h b/core/fpdfapi/page/cpdf_tilingpattern.h
index c79708d..35dd9e0 100644
--- a/core/fpdfapi/page/cpdf_tilingpattern.h
+++ b/core/fpdfapi/page/cpdf_tilingpattern.h
@@ -16,6 +16,7 @@
 class CPDF_Document;
 class CPDF_Form;
 class CPDF_Object;
+class CPDF_PageObject;
 
 class CPDF_TilingPattern final : public CPDF_Pattern {
  public:
@@ -27,20 +28,18 @@
   CPDF_TilingPattern* AsTilingPattern() override;
   CPDF_ShadingPattern* AsShadingPattern() override;
 
-  bool Load();
+  std::unique_ptr<CPDF_Form> Load(CPDF_PageObject* pPageObj);
 
   bool colored() const { return m_bColored; }
   const CFX_FloatRect& bbox() const { return m_BBox; }
   float x_step() const { return m_XStep; }
   float y_step() const { return m_YStep; }
-  CPDF_Form* form() const { return m_pForm.get(); }
 
  private:
   bool m_bColored;
   CFX_FloatRect m_BBox;
   float m_XStep;
   float m_YStep;
-  std::unique_ptr<CPDF_Form> m_pForm;
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_TILINGPATTERN_H_
diff --git a/core/fpdfapi/render/cpdf_renderstatus.cpp b/core/fpdfapi/render/cpdf_renderstatus.cpp
index 092059b..5f742ef 100644
--- a/core/fpdfapi/render/cpdf_renderstatus.cpp
+++ b/core/fpdfapi/render/cpdf_renderstatus.cpp
@@ -930,6 +930,7 @@
     CPDF_Document* pDoc,
     CPDF_PageRenderCache* pCache,
     CPDF_TilingPattern* pPattern,
+    CPDF_Form* pPatternForm,
     const CFX_Matrix& mtObject2Device,
     int width,
     int height,
@@ -958,7 +959,7 @@
   options.GetOptions().bForceHalftone = true;
 
   CPDF_RenderContext context(pDoc, pCache);
-  context.AppendLayer(pPattern->form(), &mtPattern2Bitmap);
+  context.AppendLayer(pPatternForm, &mtPattern2Bitmap);
   context.Render(&bitmap_device, &options, nullptr);
 #if defined _SKIA_SUPPORT_PATHS_
   bitmap_device.Flush(true);
@@ -2144,7 +2145,8 @@
                                           CPDF_PageObject* pPageObj,
                                           const CFX_Matrix& mtObj2Device,
                                           bool bStroke) {
-  if (!pPattern->Load())
+  const std::unique_ptr<CPDF_Form> pPatternForm = pPattern->Load(pPageObj);
+  if (!pPatternForm)
     return;
 
   CFX_RenderDevice::StateRestorer restorer(m_pDevice);
@@ -2202,7 +2204,7 @@
     if (!pPattern->colored())
       pStates = CloneObjStates(pPageObj, bStroke);
 
-    const CPDF_Dictionary* pFormDict = pPattern->form()->GetDict();
+    const CPDF_Dictionary* pFormDict = pPatternForm->GetDict();
     const CPDF_Dictionary* pFormResource =
         pFormDict ? pFormDict->GetDictFor("Resources") : nullptr;
     for (int col = min_col; col <= max_col; col++) {
@@ -2215,11 +2217,11 @@
         CFX_RenderDevice::StateRestorer restorer2(m_pDevice);
         CPDF_RenderStatus status(m_pContext.Get(), m_pDevice);
         status.SetOptions(m_Options);
-        status.SetTransparency(pPattern->form()->GetTransparency());
+        status.SetTransparency(pPatternForm->GetTransparency());
         status.SetFormResource(pFormResource);
         status.SetDropObjects(m_bDropObjects);
         status.Initialize(this, pStates.get());
-        status.RenderObjectList(pPattern->form(), matrix);
+        status.RenderObjectList(pPatternForm.get(), matrix);
       }
     }
     return;
@@ -2247,15 +2249,16 @@
   float top_offset = cell_bbox.bottom - mtPattern2Device.f;
   RetainPtr<CFX_DIBitmap> pPatternBitmap;
   if (width * height < 16) {
-    RetainPtr<CFX_DIBitmap> pEnlargedBitmap =
-        DrawPatternBitmap(m_pContext->GetDocument(), m_pContext->GetPageCache(),
-                          pPattern, mtObj2Device, 8, 8, m_Options.GetOptions());
+    RetainPtr<CFX_DIBitmap> pEnlargedBitmap = DrawPatternBitmap(
+        m_pContext->GetDocument(), m_pContext->GetPageCache(), pPattern,
+        pPatternForm.get(), mtObj2Device, 8, 8, m_Options.GetOptions());
     pPatternBitmap = pEnlargedBitmap->StretchTo(
         width, height, FXDIB_ResampleOptions(), nullptr);
   } else {
-    pPatternBitmap = DrawPatternBitmap(
-        m_pContext->GetDocument(), m_pContext->GetPageCache(), pPattern,
-        mtObj2Device, width, height, m_Options.GetOptions());
+    pPatternBitmap =
+        DrawPatternBitmap(m_pContext->GetDocument(), m_pContext->GetPageCache(),
+                          pPattern, pPatternForm.get(), mtObj2Device, width,
+                          height, m_Options.GetOptions());
   }
   if (!pPatternBitmap)
     return;
diff --git a/testing/resources/pixel/bug_1161_expected.pdf.0.png b/testing/resources/pixel/bug_1161_expected.pdf.0.png
index f54d0ec..745b8c3 100644
--- a/testing/resources/pixel/bug_1161_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1161_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1286.in b/testing/resources/pixel/bug_1286.in
new file mode 100644
index 0000000..e3d0133
--- /dev/null
+++ b/testing/resources/pixel/bug_1286.in
@@ -0,0 +1,73 @@
+{{header}}
+{{object 1 0}} <<
+    /Type /Catalog
+    /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Contents 5 0 R
+  /MediaBox [0 0 600 850]
+  /Parent 3 0 R
+  /Resources <<
+    /ColorSpace << /CS0 [/Pattern] >>
+    /ExtGState << /GS0 6 0 R /GS1 7 0 R >>
+    /Pattern << /P0 4 0 R >>
+    /ProcSet [/PDF /Text]
+  >>
+>>
+endobj
+{{object 4 0}} <<
+  /Type /Pattern
+  /BBox [0 0 72 72]
+  /PaintType 1
+  /PatternType 1
+  /Resources << /ExtGState << /GS0 7 0 R >> >>
+  /TilingType 1
+  /XStep 72
+  /YStep 72
+  {{streamlen}}
+>>
+stream
+0 0 72 72 re B
+endstream
+endobj
+{{object 5 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+/GS0 gs /Pattern cs /P0 scn
+50 700 100 100 re
+B
+Q
+
+q
+/GS1 gs /Pattern cs /P0 scn
+50 580 100 100 re
+B
+Q
+endstream
+endobj
+{{object 6 0}} <<
+  /Type /ExtGState
+  /CA 0.199997
+  /ca 0.199997
+>>
+endobj
+{{object 7 0}} <<
+  /Type /ExtGState
+  /CA 1
+  /ca 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1286_expected.pdf.0.png b/testing/resources/pixel/bug_1286_expected.pdf.0.png
new file mode 100644
index 0000000..de21a23
--- /dev/null
+++ b/testing/resources/pixel/bug_1286_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1288_1.in b/testing/resources/pixel/bug_1288_1.in
new file mode 100644
index 0000000..d7a1a7c
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_1.in
@@ -0,0 +1,62 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 200 200]
+  /Resources <<
+    /ColorSpace << /CS1 [/Pattern /DeviceRGB] >>
+    /Pattern << /P1 5 0 R >>
+  >>
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+4 w /CS1 cs 1 1 1 /P1 scn
+q 75 75 100 100 re f Q
+q 75 75 100 100 re S Q
+Q
+q
+4 w /CS1 cs 1 1 1 /P1 scn
+q 25 25 100 100 re f Q
+q 25 25 100 100 re S Q
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Pattern
+  /PatternType 1
+  /PaintType 2
+  /TilingType 3
+  /BBox [0 0 8 8]
+  /YStep 8
+  /XStep 8
+  {{streamlen}}
+>>
+stream
+q
+0 0 m 8 0 l S
+0 2 m 8 2 l S
+0 4 m 8 4 l S
+0 6 m 8 6 l S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1288_1_expected.pdf.0.png b/testing/resources/pixel/bug_1288_1_expected.pdf.0.png
new file mode 100644
index 0000000..42d98b4
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1288_2.in b/testing/resources/pixel/bug_1288_2.in
new file mode 100644
index 0000000..081c4d0
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_2.in
@@ -0,0 +1,67 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /MediaBox [0 0 300 300]
+  /Resources <<
+    /Pattern << /P0 5 0 R >>
+    /ColorSpace << /CS0 [/Pattern /DeviceRGB] >>
+  >>
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+0 TL
+q
+/CS0 cs 0 0 1 /P0 scn
+20 20 99 99 re
+f*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+  /Type /Pattern
+  /PatternType 1
+  /PaintType 2
+  /TilingType 1
+  /BBox [0 0 100 100]
+  /XStep 98
+  /YStep 4
+  {{streamlen}}
+>>
+stream
+q
+1 i
+0 0 100 100 re
+W n
+0 J 0 j 1 w 10 M [1 ]0 d
+2 4 m
+99 4 l
+1 3 m
+98 3 l
+2 2 m
+99 2 l
+1 1 m
+98 1 l
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1288_2_expected.pdf.0.png b/testing/resources/pixel/bug_1288_2_expected.pdf.0.png
new file mode 100644
index 0000000..708eada
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_2_expected.pdf.0.png
Binary files differ