XFA: Support PNG format in pdfium_test and add image diffing
[pdfium.git] / samples / pdfium_test.cc
index 2ff8f5b..fc4bbe4 100644 (file)
@@ -9,6 +9,7 @@
 #include <wchar.h>
 
 #include <list>
+#include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
@@ -20,6 +21,7 @@
 #include "../fpdfsdk/include/fpdfview.h"
 #include "../core/include/fxcrt/fx_system.h"
 #include "v8/include/v8.h"
+#include "image_diff_png.h"
 
 #ifdef _WIN32
 #define snprintf _snprintf
@@ -31,6 +33,7 @@
 enum OutputFormat {
   OUTPUT_NONE,
   OUTPUT_PPM,
+  OUTPUT_PNG,
 #ifdef _WIN32
   OUTPUT_BMP,
   OUTPUT_EMF,
@@ -41,6 +44,7 @@ struct Options {
   Options() : output_format(OUTPUT_NONE) { }
 
   OutputFormat output_format;
+  std::string scale_factor_as_string;
   std::string exe_path;
   std::string bin_directory;
 };
@@ -111,14 +115,21 @@ static bool GetExternalData(const Options& options,
 }
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
 
+static bool CheckDimensions(int stride, int width, int height) {
+  if (stride < 0 || width < 0 || height < 0)
+    return false;
+  if (height > 0 && width > INT_MAX / height)
+    return false;
+  return true;
+}
+
 static void WritePpm(const char* pdf_name, int num, const void* buffer_void,
                      int stride, int width, int height) {
   const char* buffer = reinterpret_cast<const char*>(buffer_void);
 
-  if (stride < 0 || width < 0 || height < 0)
-    return;
-  if (height > 0 && width > INT_MAX / height)
+  if (!CheckDimensions(stride, width, height))
     return;
+
   int out_len = width * height;
   if (out_len > INT_MAX / 3)
     return;
@@ -152,13 +163,48 @@ static void WritePpm(const char* pdf_name, int num, const void* buffer_void,
   fclose(fp);
 }
 
+static void WritePng(const char* pdf_name, int num, const void* buffer_void,
+                     int stride, int width, int height) {
+  if (!CheckDimensions(stride, width, height))
+    return;
+
+  std::vector<unsigned char> png_encoding;
+  const unsigned char* buffer = static_cast<const unsigned char*>(buffer_void);
+  if (!image_diff_png::EncodeBGRAPNG(
+          buffer, width, height, stride, false, &png_encoding)) {
+    fprintf(stderr, "Failed to convert bitmap to PNG\n");
+    return;
+  }
+
+  char filename[256];
+  int chars_formatted = snprintf(
+      filename, sizeof(filename), "%s.%d.png", pdf_name, num);
+  if (chars_formatted < 0 ||
+      static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
+    fprintf(stderr, "Filname %s is too long\n", filename);
+    return;
+  }
+
+  FILE* fp = fopen(filename, "wb");
+  if (!fp) {
+    fprintf(stderr, "Failed to open %s for output\n", filename);
+    return;
+  }
+
+  size_t bytes_written = fwrite(
+      &png_encoding.front(), 1, png_encoding.size(), fp);
+  if (bytes_written != png_encoding.size())
+    fprintf(stderr, "Failed to write to  %s\n", filename);
+
+  (void) fclose(fp);
+}
+
 #ifdef _WIN32
 static void WriteBmp(const char* pdf_name, int num, const void* buffer,
                      int stride, int width, int height) {
-  if (stride < 0 || width < 0 || height < 0)
-    return;
-  if (height > 0 && width > INT_MAX / height)
+  if (!CheckDimensions(stride, width, height))
     return;
+
   int out_len = stride * height;
   if (out_len > INT_MAX / 3)
     return;
@@ -197,9 +243,9 @@ void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
   snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
 
   HDC dc = CreateEnhMetaFileA(NULL, filename, NULL, NULL);
-  
-  HRGN rgn = CreateRectRgn(0, 0, width, height); 
-  SelectClipRgn(dc, rgn); 
+
+  HRGN rgn = CreateRectRgn(0, 0, width, height);
+  SelectClipRgn(dc, rgn);
   DeleteObject(rgn);
 
   SelectObject(dc, GetStockObject(NULL_PEN));
@@ -291,6 +337,12 @@ bool ParseCommandLine(const std::vector<std::string>& args,
         return false;
       }
       options->output_format = OUTPUT_PPM;
+    } else if (cur_arg == "--png") {
+      if (options->output_format != OUTPUT_NONE) {
+        fprintf(stderr, "Duplicate or conflicting --png argument\n");
+        return false;
+      }
+      options->output_format = OUTPUT_PNG;
     }
 #ifdef _WIN32
     else if (cur_arg == "--emf") {
@@ -317,6 +369,13 @@ bool ParseCommandLine(const std::vector<std::string>& args,
       options->bin_directory = cur_arg.substr(10);
     }
 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
+    else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--scale=") == 0) {
+      if (!options->scale_factor_as_string.empty()) {
+        fprintf(stderr, "Duplicate --scale argument\n");
+        return false;
+      }
+      options->scale_factor_as_string = cur_arg.substr(8);
+    }
     else
       break;
   }
@@ -358,7 +417,7 @@ void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
 }
 
 void RenderPdf(const std::string& name, const char* pBuf, size_t len,
-               OutputFormat format) {
+               const Options& options) {
   printf("Rendering PDF file %s.\n", name.c_str());
 
   IPDF_JSPLATFORM platform_callbacks;
@@ -437,9 +496,15 @@ void RenderPdf(const std::string& name, const char* pBuf, size_t len,
 
     int width = static_cast<int>(FPDF_GetPageWidth(page));
     int height = static_cast<int>(FPDF_GetPageHeight(page));
+    if (!options.scale_factor_as_string.empty()) {
+      double scale = 1.0;
+      std::stringstream(options.scale_factor_as_string) >> scale;
+      width *= scale;
+      height *= scale;
+    }
+
     FPDF_BITMAP bitmap = FPDFBitmap_Create(width, height, 0);
     FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF);
-
     FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, 0, 0);
     rendered_pages ++;
 
@@ -448,7 +513,7 @@ void RenderPdf(const std::string& name, const char* pBuf, size_t len,
     const char* buffer =
         reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap));
 
-    switch (format) {
+    switch (options.output_format) {
 #ifdef _WIN32
       case OUTPUT_BMP:
         WriteBmp(name.c_str(), i, buffer, stride, width, height);
@@ -461,6 +526,11 @@ void RenderPdf(const std::string& name, const char* pBuf, size_t len,
       case OUTPUT_PPM:
         WritePpm(name.c_str(), i, buffer, stride, width, height);
         break;
+
+      case OUTPUT_PNG:
+        WritePng(name.c_str(), i, buffer, stride, width, height);
+        break;
+
       default:
         break;
     }
@@ -489,6 +559,8 @@ int main(int argc, const char* argv[]) {
   if (!ParseCommandLine(args, &options, &files)) {
     printf("Usage: pdfium_test [OPTION] [FILE]...\n");
     printf("--bin-dir=<path> - override path to v8 external data\n");
+    printf("--scale=<number> - scale output size by number (e.g. 0.5)\n");
+    printf("--png - write page images <pdf-name>.<page-number>.png\n");
     printf("--ppm - write page images <pdf-name>.<page-number>.ppm\n");
 #ifdef _WIN32
     printf("--bmp - write page images <pdf-name>.<page-number>.bmp\n");
@@ -526,7 +598,7 @@ int main(int argc, const char* argv[]) {
     char* file_contents = GetFileContents(filename.c_str(), &file_length);
     if (!file_contents)
       continue;
-    RenderPdf(filename, file_contents, file_length, options.output_format);
+    RenderPdf(filename, file_contents, file_length, options);
     free(file_contents);
   }