Move helpers to //samples/helpers

Moves various helper sources from //samples to //samples/helpers, with
the goal of keeping //samples more focused on high-level concepts.

Bug: pdfium:2059
Change-Id: I4e12e8c4f9fa1c869df7355bec132f717b0b966a
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/109230
Commit-Queue: K. Moon <kmoon@chromium.org>
Reviewed-by: Nigi <nigi@chromium.org>
diff --git a/samples/BUILD.gn b/samples/BUILD.gn
index ccf7ff8..83b62dd 100644
--- a/samples/BUILD.gn
+++ b/samples/BUILD.gn
@@ -33,13 +33,15 @@
 executable("pdfium_test") {
   testonly = true
   sources = [
+    "helpers/dump.cc",
+    "helpers/dump.h",
+    "helpers/event.cc",
+    "helpers/event.h",
+    "helpers/page_renderer.cc",
+    "helpers/page_renderer.h",
+    "helpers/write.cc",
+    "helpers/write.h",
     "pdfium_test.cc",
-    "pdfium_test_dump_helper.cc",
-    "pdfium_test_dump_helper.h",
-    "pdfium_test_event_helper.cc",
-    "pdfium_test_event_helper.h",
-    "pdfium_test_write_helper.cc",
-    "pdfium_test_write_helper.h",
   ]
 
   # Note: One should write programs that depend on ../:pdfium. Whereas this
diff --git a/samples/pdfium_test_dump_helper.cc b/samples/helpers/dump.cc
similarity index 96%
rename from samples/pdfium_test_dump_helper.cc
rename to samples/helpers/dump.cc
index 4d45390..e2a8469 100644
--- a/samples/pdfium_test_dump_helper.cc
+++ b/samples/helpers/dump.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "samples/pdfium_test_dump_helper.h"
+#include "samples/helpers/dump.h"
 
 #include <limits.h>
 #include <string.h>
@@ -98,15 +98,17 @@
   static const size_t kBufSize = 1024;
   unsigned short buf[kBufSize];
   unsigned long len = FPDF_StructElement_GetType(child, buf, kBufSize);
-  if (len > 0)
+  if (len > 0) {
     printf("%*s S: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str());
+  }
 
   int attr_count = FPDF_StructElement_GetAttributeCount(child);
   for (int i = 0; i < attr_count; i++) {
     FPDF_STRUCTELEMENT_ATTR child_attr =
         FPDF_StructElement_GetAttributeAtIndex(child, i);
-    if (!child_attr)
+    if (!child_attr) {
       continue;
+    }
     printf("%*s A[%d]:\n", indent * 2, "", i);
     DumpStructureElementAttributes(child_attr, indent * 2 + 2);
   }
@@ -127,8 +129,9 @@
 
   memset(buf, 0, sizeof(buf));
   len = FPDF_StructElement_GetID(child, buf, kBufSize);
-  if (len > 0)
+  if (len > 0) {
     printf("%*s ID: %ls\n", indent * 2, "", ConvertToWString(buf, len).c_str());
+  }
 
   memset(buf, 0, sizeof(buf));
   len = FPDF_StructElement_GetLang(child, buf, kBufSize);
@@ -138,8 +141,9 @@
   }
 
   int mcid = FPDF_StructElement_GetMarkedContentID(child);
-  if (mcid != -1)
+  if (mcid != -1) {
     printf("%*s MCID: %d\n", indent * 2, "", mcid);
+  }
 
   FPDF_STRUCTELEMENT parent = FPDF_StructElement_GetParent(child);
   if (parent) {
@@ -169,8 +173,9 @@
     FPDF_STRUCTELEMENT sub_child = FPDF_StructElement_GetChildAtIndex(child, i);
     // If the child is not an Element then this will return null. This can
     // happen if the element is things like an object reference or a stream.
-    if (!sub_child)
+    if (!sub_child) {
       continue;
+    }
 
     DumpChildStructure(sub_child, indent + 1);
   }
@@ -211,8 +216,9 @@
     char meta_buffer[4096];
     unsigned long len =
         FPDF_GetMetaText(doc, meta_tag, meta_buffer, sizeof(meta_buffer));
-    if (!len)
+    if (!len) {
       continue;
+    }
 
     auto* meta_string = reinterpret_cast<unsigned short*>(meta_buffer);
     printf("%-12s = %ls (%lu bytes)\n", meta_tag,
diff --git a/samples/pdfium_test_dump_helper.h b/samples/helpers/dump.h
similarity index 73%
rename from samples/pdfium_test_dump_helper.h
rename to samples/helpers/dump.h
index 727731e..6682b29 100644
--- a/samples/pdfium_test_dump_helper.h
+++ b/samples/helpers/dump.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
-#define SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
+#ifndef SAMPLES_HELPERS_DUMP_H_
+#define SAMPLES_HELPERS_DUMP_H_
 
 #include "public/fpdfview.h"
 
@@ -12,4 +12,4 @@
 void DumpPageStructure(FPDF_PAGE page, int page_idx);
 void DumpMetaData(FPDF_DOCUMENT doc);
 
-#endif  // SAMPLES_PDFIUM_TEST_DUMP_HELPER_H_
+#endif  // SAMPLES_HELPERS_DUMP_H_
diff --git a/samples/pdfium_test_event_helper.cc b/samples/helpers/event.cc
similarity index 92%
rename from samples/pdfium_test_event_helper.cc
rename to samples/helpers/event.cc
index d55057e..48491e8 100644
--- a/samples/pdfium_test_event_helper.cc
+++ b/samples/helpers/event.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "samples/pdfium_test_event_helper.h"
+#include "samples/helpers/event.h"
 
 #include <stdio.h>
 
@@ -17,12 +17,15 @@
 
 uint32_t GetModifiers(std::string modifiers_string) {
   uint32_t modifiers = 0;
-  if (modifiers_string.find("shift") != std::string::npos)
+  if (modifiers_string.find("shift") != std::string::npos) {
     modifiers |= FWL_EVENTFLAG_ShiftKey;
-  if (modifiers_string.find("control") != std::string::npos)
+  }
+  if (modifiers_string.find("control") != std::string::npos) {
     modifiers |= FWL_EVENTFLAG_ControlKey;
-  if (modifiers_string.find("alt") != std::string::npos)
+  }
+  if (modifiers_string.find("alt") != std::string::npos) {
     modifiers |= FWL_EVENTFLAG_AltKey;
+  }
 
   return modifiers;
 }
@@ -65,12 +68,13 @@
   int y = atoi(tokens[3].c_str());
   uint32_t modifiers = tokens.size() >= 5 ? GetModifiers(tokens[4]) : 0;
 
-  if (tokens[1] == "left")
+  if (tokens[1] == "left") {
     FORM_OnLButtonDown(form, page, modifiers, x, y);
-  else if (tokens[1] == "right")
+  } else if (tokens[1] == "right") {
     FORM_OnRButtonDown(form, page, modifiers, x, y);
-  else
+  } else {
     fprintf(stderr, "mousedown: bad button name\n");
+  }
 }
 
 void SendMouseUpEvent(FPDF_FORMHANDLE form,
@@ -84,12 +88,13 @@
   int x = atoi(tokens[2].c_str());
   int y = atoi(tokens[3].c_str());
   int modifiers = tokens.size() >= 5 ? GetModifiers(tokens[4]) : 0;
-  if (tokens[1] == "left")
+  if (tokens[1] == "left") {
     FORM_OnLButtonUp(form, page, modifiers, x, y);
-  else if (tokens[1] == "right")
+  } else if (tokens[1] == "right") {
     FORM_OnRButtonUp(form, page, modifiers, x, y);
-  else
+  } else {
     fprintf(stderr, "mouseup: bad button name\n");
+  }
 }
 
 void SendMouseDoubleClickEvent(FPDF_FORMHANDLE form,
@@ -161,8 +166,9 @@
   auto lines = StringSplit(events, '\n');
   for (const auto& line : lines) {
     auto command = StringSplit(line, '#');
-    if (command[0].empty())
+    if (command[0].empty()) {
       continue;
+    }
     auto tokens = StringSplit(command[0], ',');
     if (tokens[0] == "charcode") {
       SendCharCodeEvent(form, page, tokens);
diff --git a/samples/pdfium_test_event_helper.h b/samples/helpers/event.h
similarity index 75%
rename from samples/pdfium_test_event_helper.h
rename to samples/helpers/event.h
index 2f2825a..021c075 100644
--- a/samples/pdfium_test_event_helper.h
+++ b/samples/helpers/event.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
-#define SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
+#ifndef SAMPLES_HELPERS_EVENT_H_
+#define SAMPLES_HELPERS_EVENT_H_
 
 #include <functional>
 #include <string>
@@ -16,4 +16,4 @@
                     const std::string& events,
                     const std::function<void()>& idler);
 
-#endif  // SAMPLES_PDFIUM_TEST_EVENT_HELPER_H_
+#endif  // SAMPLES_HELPERS_EVENT_H_
diff --git a/samples/helpers/page_renderer.cc b/samples/helpers/page_renderer.cc
new file mode 100644
index 0000000..e7771ef
--- /dev/null
+++ b/samples/helpers/page_renderer.cc
@@ -0,0 +1,16 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "samples/helpers/page_renderer.h"
+
+#include "public/fpdfview.h"
+
+PageRenderer::PageRenderer(FPDF_PAGE page, int width, int height, int flags)
+    : page_(page), width_(width), height_(height), flags_(flags) {}
+
+PageRenderer::~PageRenderer() = default;
+
+bool PageRenderer::Continue() {
+  return false;
+}
diff --git a/samples/helpers/page_renderer.h b/samples/helpers/page_renderer.h
new file mode 100644
index 0000000..277bd0f
--- /dev/null
+++ b/samples/helpers/page_renderer.h
@@ -0,0 +1,47 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAMPLES_HELPERS_PAGE_RENDERER_H_
+#define SAMPLES_HELPERS_PAGE_RENDERER_H_
+
+#include <string>
+
+#include "public/fpdfview.h"
+
+// Renderer for a single page.
+class PageRenderer {
+ public:
+  virtual ~PageRenderer();
+
+  // Returns `true` if the rendered output exists. Must call `Finish()` first.
+  virtual bool HasOutput() const = 0;
+
+  // Starts rendering the page, returning `false` on failure.
+  virtual bool Start() = 0;
+
+  // Continues rendering the page, returning `false` when complete.
+  virtual bool Continue();
+
+  // Finishes rendering the page.
+  virtual void Finish(FPDF_FORMHANDLE form) = 0;
+
+  // Writes rendered output to a file, returning `false` on failure.
+  virtual bool Write(const std::string& name, int page_index, bool md5) = 0;
+
+ protected:
+  PageRenderer(FPDF_PAGE page, int width, int height, int flags);
+
+  FPDF_PAGE page() { return page_; }
+  int width() const { return width_; }
+  int height() const { return height_; }
+  int flags() const { return flags_; }
+
+ private:
+  FPDF_PAGE page_;
+  int width_;
+  int height_;
+  int flags_;
+};
+
+#endif  // SAMPLES_HELPERS_PAGE_RENDERER_H_
diff --git a/samples/pdfium_test_write_helper.cc b/samples/helpers/write.cc
similarity index 91%
rename from samples/pdfium_test_write_helper.cc
rename to samples/helpers/write.cc
index 9d57eb2..aeeac25 100644
--- a/samples/pdfium_test_write_helper.cc
+++ b/samples/helpers/write.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "samples/pdfium_test_write_helper.h"
+#include "samples/helpers/write.h"
 
 #include <limits.h>
 
@@ -28,111 +28,155 @@
 namespace {
 
 bool CheckDimensions(int stride, int width, int height) {
-  if (stride < 0 || width < 0 || height < 0)
+  if (stride < 0 || width < 0 || height < 0) {
     return false;
-  if (height > 0 && stride > INT_MAX / height)
+  }
+  if (height > 0 && stride > INT_MAX / height) {
     return false;
+  }
   return true;
 }
 
 const char* AnnotSubtypeToCString(FPDF_ANNOTATION_SUBTYPE subtype) {
-  if (subtype == FPDF_ANNOT_TEXT)
+  if (subtype == FPDF_ANNOT_TEXT) {
     return "Text";
-  if (subtype == FPDF_ANNOT_LINK)
+  }
+  if (subtype == FPDF_ANNOT_LINK) {
     return "Link";
-  if (subtype == FPDF_ANNOT_FREETEXT)
+  }
+  if (subtype == FPDF_ANNOT_FREETEXT) {
     return "FreeText";
-  if (subtype == FPDF_ANNOT_LINE)
+  }
+  if (subtype == FPDF_ANNOT_LINE) {
     return "Line";
-  if (subtype == FPDF_ANNOT_SQUARE)
+  }
+  if (subtype == FPDF_ANNOT_SQUARE) {
     return "Square";
-  if (subtype == FPDF_ANNOT_CIRCLE)
+  }
+  if (subtype == FPDF_ANNOT_CIRCLE) {
     return "Circle";
-  if (subtype == FPDF_ANNOT_POLYGON)
+  }
+  if (subtype == FPDF_ANNOT_POLYGON) {
     return "Polygon";
-  if (subtype == FPDF_ANNOT_POLYLINE)
+  }
+  if (subtype == FPDF_ANNOT_POLYLINE) {
     return "PolyLine";
-  if (subtype == FPDF_ANNOT_HIGHLIGHT)
+  }
+  if (subtype == FPDF_ANNOT_HIGHLIGHT) {
     return "Highlight";
-  if (subtype == FPDF_ANNOT_UNDERLINE)
+  }
+  if (subtype == FPDF_ANNOT_UNDERLINE) {
     return "Underline";
-  if (subtype == FPDF_ANNOT_SQUIGGLY)
+  }
+  if (subtype == FPDF_ANNOT_SQUIGGLY) {
     return "Squiggly";
-  if (subtype == FPDF_ANNOT_STRIKEOUT)
+  }
+  if (subtype == FPDF_ANNOT_STRIKEOUT) {
     return "StrikeOut";
-  if (subtype == FPDF_ANNOT_STAMP)
+  }
+  if (subtype == FPDF_ANNOT_STAMP) {
     return "Stamp";
-  if (subtype == FPDF_ANNOT_CARET)
+  }
+  if (subtype == FPDF_ANNOT_CARET) {
     return "Caret";
-  if (subtype == FPDF_ANNOT_INK)
+  }
+  if (subtype == FPDF_ANNOT_INK) {
     return "Ink";
-  if (subtype == FPDF_ANNOT_POPUP)
+  }
+  if (subtype == FPDF_ANNOT_POPUP) {
     return "Popup";
-  if (subtype == FPDF_ANNOT_FILEATTACHMENT)
+  }
+  if (subtype == FPDF_ANNOT_FILEATTACHMENT) {
     return "FileAttachment";
-  if (subtype == FPDF_ANNOT_SOUND)
+  }
+  if (subtype == FPDF_ANNOT_SOUND) {
     return "Sound";
-  if (subtype == FPDF_ANNOT_MOVIE)
+  }
+  if (subtype == FPDF_ANNOT_MOVIE) {
     return "Movie";
-  if (subtype == FPDF_ANNOT_WIDGET)
+  }
+  if (subtype == FPDF_ANNOT_WIDGET) {
     return "Widget";
-  if (subtype == FPDF_ANNOT_SCREEN)
+  }
+  if (subtype == FPDF_ANNOT_SCREEN) {
     return "Screen";
-  if (subtype == FPDF_ANNOT_PRINTERMARK)
+  }
+  if (subtype == FPDF_ANNOT_PRINTERMARK) {
     return "PrinterMark";
-  if (subtype == FPDF_ANNOT_TRAPNET)
+  }
+  if (subtype == FPDF_ANNOT_TRAPNET) {
     return "TrapNet";
-  if (subtype == FPDF_ANNOT_WATERMARK)
+  }
+  if (subtype == FPDF_ANNOT_WATERMARK) {
     return "Watermark";
-  if (subtype == FPDF_ANNOT_THREED)
+  }
+  if (subtype == FPDF_ANNOT_THREED) {
     return "3D";
-  if (subtype == FPDF_ANNOT_RICHMEDIA)
+  }
+  if (subtype == FPDF_ANNOT_RICHMEDIA) {
     return "RichMedia";
-  if (subtype == FPDF_ANNOT_XFAWIDGET)
+  }
+  if (subtype == FPDF_ANNOT_XFAWIDGET) {
     return "XFAWidget";
+  }
   NOTREACHED_NORETURN();
 }
 
 void AppendFlagString(const char* flag, std::string* output) {
-  if (!output->empty())
+  if (!output->empty()) {
     *output += ", ";
+  }
   *output += flag;
 }
 
 std::string AnnotFlagsToString(int flags) {
   std::string str;
-  if (flags & FPDF_ANNOT_FLAG_INVISIBLE)
+  if (flags & FPDF_ANNOT_FLAG_INVISIBLE) {
     AppendFlagString("Invisible", &str);
-  if (flags & FPDF_ANNOT_FLAG_HIDDEN)
+  }
+  if (flags & FPDF_ANNOT_FLAG_HIDDEN) {
     AppendFlagString("Hidden", &str);
-  if (flags & FPDF_ANNOT_FLAG_PRINT)
+  }
+  if (flags & FPDF_ANNOT_FLAG_PRINT) {
     AppendFlagString("Print", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOZOOM)
+  }
+  if (flags & FPDF_ANNOT_FLAG_NOZOOM) {
     AppendFlagString("NoZoom", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOROTATE)
+  }
+  if (flags & FPDF_ANNOT_FLAG_NOROTATE) {
     AppendFlagString("NoRotate", &str);
-  if (flags & FPDF_ANNOT_FLAG_NOVIEW)
+  }
+  if (flags & FPDF_ANNOT_FLAG_NOVIEW) {
     AppendFlagString("NoView", &str);
-  if (flags & FPDF_ANNOT_FLAG_READONLY)
+  }
+  if (flags & FPDF_ANNOT_FLAG_READONLY) {
     AppendFlagString("ReadOnly", &str);
-  if (flags & FPDF_ANNOT_FLAG_LOCKED)
+  }
+  if (flags & FPDF_ANNOT_FLAG_LOCKED) {
     AppendFlagString("Locked", &str);
-  if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW)
+  }
+  if (flags & FPDF_ANNOT_FLAG_TOGGLENOVIEW) {
     AppendFlagString("ToggleNoView", &str);
+  }
   return str;
 }
 
 const char* PageObjectTypeToCString(int type) {
-  if (type == FPDF_PAGEOBJ_TEXT)
+  if (type == FPDF_PAGEOBJ_TEXT) {
     return "Text";
-  if (type == FPDF_PAGEOBJ_PATH)
+  }
+  if (type == FPDF_PAGEOBJ_PATH) {
     return "Path";
-  if (type == FPDF_PAGEOBJ_IMAGE)
+  }
+  if (type == FPDF_PAGEOBJ_IMAGE) {
     return "Image";
-  if (type == FPDF_PAGEOBJ_SHADING)
+  }
+  if (type == FPDF_PAGEOBJ_SHADING) {
     return "Shading";
-  if (type == FPDF_PAGEOBJ_FORM)
+  }
+  if (type == FPDF_PAGEOBJ_FORM) {
     return "Form";
+  }
   NOTREACHED_NORETURN();
 }
 
@@ -519,8 +563,9 @@
     return;
   }
   FILE* fp = fopen(filename.c_str(), "wb");
-  if (!fp)
+  if (!fp) {
     return;
+  }
 
   HDC dc = CreateEnhMetaFileA(nullptr, nullptr, nullptr, nullptr);
 
@@ -532,8 +577,9 @@
   std::vector<const ENHMETARECORD*> items;
   EnumEnhMetaFile(nullptr, emf, &EnhMetaFileProc, &items, nullptr);
   for (const ENHMETARECORD* record : items) {
-    if (record->iType != EMR_GDICOMMENT)
+    if (record->iType != EMR_GDICOMMENT) {
       continue;
+    }
 
     const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
     const char* data = reinterpret_cast<const char*>(comment->Data);
@@ -602,24 +648,27 @@
   }
 
   size_t bytes_written = fwrite(buf, 1, buflen, fp);
-  if (bytes_written == buflen)
+  if (bytes_written == buflen) {
     fprintf(stderr, "Successfully wrote %s %s.\n", filetype, filename);
-  else
+  } else {
     fprintf(stderr, "Failed to write to %s.\n", filename);
+  }
   fclose(fp);
 }
 
 std::vector<uint8_t> EncodeBitmapToPng(ScopedFPDFBitmap bitmap) {
   std::vector<uint8_t> png_encoding;
   int format = FPDFBitmap_GetFormat(bitmap.get());
-  if (format == FPDFBitmap_Unknown)
+  if (format == FPDFBitmap_Unknown) {
     return png_encoding;
+  }
 
   int width = FPDFBitmap_GetWidth(bitmap.get());
   int height = FPDFBitmap_GetHeight(bitmap.get());
   int stride = FPDFBitmap_GetStride(bitmap.get());
-  if (!CheckDimensions(stride, width, height))
+  if (!CheckDimensions(stride, width, height)) {
     return png_encoding;
+  }
 
   auto input = pdfium::make_span(
       static_cast<const uint8_t*>(FPDFBitmap_GetBuffer(bitmap.get())),
@@ -640,8 +689,9 @@
       std::vector<FPDF_WCHAR> buf = GetFPDFWideStringBuffer(length_bytes);
       unsigned long actual_length_bytes =
           FPDFAttachment_GetName(attachment, buf.data(), length_bytes);
-      if (actual_length_bytes == length_bytes)
+      if (actual_length_bytes == length_bytes) {
         attachment_name = GetPlatformString(buf.data());
+      }
     }
     if (attachment_name.empty()) {
       fprintf(stderr, "Attachment #%d has an empty file name.\n", i + 1);
diff --git a/samples/pdfium_test_write_helper.h b/samples/helpers/write.h
similarity index 92%
rename from samples/pdfium_test_write_helper.h
rename to samples/helpers/write.h
index ffeba1f..20212ad 100644
--- a/samples/pdfium_test_write_helper.h
+++ b/samples/helpers/write.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
-#define SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
+#ifndef SAMPLES_HELPERS_WRITE_H_
+#define SAMPLES_HELPERS_WRITE_H_
 
 #include <string>
 
@@ -57,4 +57,4 @@
                              int page_num);
 void WriteThumbnail(FPDF_PAGE page, const char* pdf_name, int page_num);
 
-#endif  // SAMPLES_PDFIUM_TEST_WRITE_HELPER_H_
+#endif  // SAMPLES_HELPERS_WRITE_H_
diff --git a/samples/pdfium_test.cc b/samples/pdfium_test.cc
index e72d828..e724ba2 100644
--- a/samples/pdfium_test.cc
+++ b/samples/pdfium_test.cc
@@ -32,9 +32,10 @@
 #include "public/fpdf_structtree.h"
 #include "public/fpdf_text.h"
 #include "public/fpdfview.h"
-#include "samples/pdfium_test_dump_helper.h"
-#include "samples/pdfium_test_event_helper.h"
-#include "samples/pdfium_test_write_helper.h"
+#include "samples/helpers/dump.h"
+#include "samples/helpers/event.h"
+#include "samples/helpers/page_renderer.h"
+#include "samples/helpers/write.h"
 #include "testing/command_line_helpers.h"
 #include "testing/font_renamer.h"
 #include "testing/fx_string_testhelpers.h"
@@ -857,42 +858,6 @@
   FPDF_FORMFILLINFO_PDFiumTest* form_fill_info_;
 };
 
-// Renderer for a single page.
-class PageRenderer {
- public:
-  virtual ~PageRenderer() = default;
-
-  // Returns `true` if the rendered output exists. Must call `Finish()` first.
-  virtual bool HasOutput() const = 0;
-
-  // Starts rendering the page, returning `false` on failure.
-  virtual bool Start() = 0;
-
-  // Continues rendering the page, returning `false` when complete.
-  virtual bool Continue() { return false; }
-
-  // Finishes rendering the page.
-  virtual void Finish(FPDF_FORMHANDLE form) = 0;
-
-  // Writes rendered output to a file, returning `false` on failure.
-  virtual bool Write(const std::string& name, int page_index, bool md5) = 0;
-
- protected:
-  PageRenderer(FPDF_PAGE page, int width, int height, int flags)
-      : page_(page), width_(width), height_(height), flags_(flags) {}
-
-  FPDF_PAGE page() { return page_; }
-  int width() const { return width_; }
-  int height() const { return height_; }
-  int flags() const { return flags_; }
-
- private:
-  FPDF_PAGE page_;
-  int width_;
-  int height_;
-  int flags_;
-};
-
 // Page renderer with bitmap output.
 class BitmapPageRenderer : public PageRenderer {
  public: