Home | History | Annotate | Download | only in samples
      1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <limits.h>
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 
     10 #include <map>
     11 #include <sstream>
     12 #include <string>
     13 #include <utility>
     14 #include <vector>
     15 
     16 #if defined PDF_ENABLE_SKIA && !defined _SKIA_SUPPORT_
     17 #define _SKIA_SUPPORT_
     18 #endif
     19 
     20 #include "core/fdrm/crypto/fx_crypt.h"
     21 #include "public/fpdf_dataavail.h"
     22 #include "public/fpdf_edit.h"
     23 #include "public/fpdf_ext.h"
     24 #include "public/fpdf_formfill.h"
     25 #include "public/fpdf_text.h"
     26 #include "public/fpdfview.h"
     27 #include "samples/image_diff_png.h"
     28 #include "testing/test_support.h"
     29 
     30 #ifdef _WIN32
     31 #include <io.h>
     32 #else
     33 #include <unistd.h>
     34 #endif
     35 
     36 #ifdef PDF_ENABLE_V8
     37 #include "v8/include/libplatform/libplatform.h"
     38 #include "v8/include/v8.h"
     39 #endif  // PDF_ENABLE_V8
     40 
     41 #ifdef PDF_ENABLE_SKIA
     42 #include "third_party/skia/include/core/SkPictureRecorder.h"
     43 #include "third_party/skia/include/core/SkStream.h"
     44 #endif
     45 
     46 #ifdef _WIN32
     47 #define access _access
     48 #define snprintf _snprintf
     49 #define R_OK 4
     50 #endif
     51 
     52 enum OutputFormat {
     53   OUTPUT_NONE,
     54   OUTPUT_TEXT,
     55   OUTPUT_PPM,
     56   OUTPUT_PNG,
     57 #ifdef _WIN32
     58   OUTPUT_BMP,
     59   OUTPUT_EMF,
     60 #endif
     61 #ifdef PDF_ENABLE_SKIA
     62   OUTPUT_SKP,
     63 #endif
     64 };
     65 
     66 struct Options {
     67   Options()
     68       : show_config(false),
     69         send_events(false),
     70         pages(false),
     71         md5(false),
     72         output_format(OUTPUT_NONE) {}
     73 
     74   bool show_config;
     75   bool send_events;
     76   bool pages;
     77   bool md5;
     78   OutputFormat output_format;
     79   std::string scale_factor_as_string;
     80   std::string exe_path;
     81   std::string bin_directory;
     82   std::string font_directory;
     83   // 0-based page numbers to be rendered.
     84   int first_page;
     85   int last_page;
     86 };
     87 
     88 struct FPDF_FORMFILLINFO_PDFiumTest : public FPDF_FORMFILLINFO {
     89   // Hold a map of the currently loaded pages in order to avoid them
     90   // to get loaded twice.
     91   std::map<int, FPDF_PAGE> loaded_pages;
     92 
     93   // Hold a pointer of FPDF_FORMHANDLE so that PDFium app hooks can
     94   // make use of it.
     95   FPDF_FORMHANDLE form_handle;
     96 };
     97 
     98 struct AvailDeleter {
     99   inline void operator()(FPDF_AVAIL avail) const { FPDFAvail_Destroy(avail); }
    100 };
    101 
    102 static FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo(
    103     FPDF_FORMFILLINFO* form_fill_info) {
    104   return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
    105 }
    106 
    107 static bool CheckDimensions(int stride, int width, int height) {
    108   if (stride < 0 || width < 0 || height < 0)
    109     return false;
    110   if (height > 0 && width > INT_MAX / height)
    111     return false;
    112   return true;
    113 }
    114 
    115 static void OutputMD5Hash(const char* file_name, const char* buffer, int len) {
    116   // Get the MD5 hash and write it to stdout.
    117   uint8_t digest[16];
    118   CRYPT_MD5Generate(reinterpret_cast<const uint8_t*>(buffer), len, digest);
    119   printf("MD5:%s:", file_name);
    120   for (int i = 0; i < 16; i++)
    121     printf("%02x", digest[i]);
    122   printf("\n");
    123 }
    124 
    125 static std::string WritePpm(const char* pdf_name,
    126                             int num,
    127                             const void* buffer_void,
    128                             int stride,
    129                             int width,
    130                             int height) {
    131   const char* buffer = reinterpret_cast<const char*>(buffer_void);
    132 
    133   if (!CheckDimensions(stride, width, height))
    134     return "";
    135 
    136   int out_len = width * height;
    137   if (out_len > INT_MAX / 3)
    138     return "";
    139   out_len *= 3;
    140 
    141   char filename[256];
    142   snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
    143   FILE* fp = fopen(filename, "wb");
    144   if (!fp)
    145     return "";
    146   fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
    147   // Source data is B, G, R, unused.
    148   // Dest data is R, G, B.
    149   std::vector<char> result(out_len);
    150   for (int h = 0; h < height; ++h) {
    151     const char* src_line = buffer + (stride * h);
    152     char* dest_line = result.data() + (width * h * 3);
    153     for (int w = 0; w < width; ++w) {
    154       // R
    155       dest_line[w * 3] = src_line[(w * 4) + 2];
    156       // G
    157       dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
    158       // B
    159       dest_line[(w * 3) + 2] = src_line[w * 4];
    160     }
    161   }
    162   fwrite(result.data(), out_len, 1, fp);
    163   fclose(fp);
    164   return std::string(filename);
    165 }
    166 
    167 void WriteText(FPDF_PAGE page, const char* pdf_name, int num) {
    168   char filename[256];
    169   int chars_formatted =
    170       snprintf(filename, sizeof(filename), "%s.%d.txt", pdf_name, num);
    171   if (chars_formatted < 0 ||
    172       static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
    173     fprintf(stderr, "Filename %s is too long\n", filename);
    174     return;
    175   }
    176 
    177   FILE* fp = fopen(filename, "w");
    178   if (!fp) {
    179     fprintf(stderr, "Failed to open %s for output\n", filename);
    180     return;
    181   }
    182 
    183   // Output in UTF32-LE.
    184   uint32_t bom = 0x0000FEFF;
    185   fwrite(&bom, sizeof(bom), 1, fp);
    186 
    187   FPDF_TEXTPAGE textpage = FPDFText_LoadPage(page);
    188   for (int i = 0; i < FPDFText_CountChars(textpage); i++) {
    189     uint32_t c = FPDFText_GetUnicode(textpage, i);
    190     fwrite(&c, sizeof(c), 1, fp);
    191   }
    192 
    193   FPDFText_ClosePage(textpage);
    194 
    195   (void)fclose(fp);
    196 }
    197 
    198 static std::string WritePng(const char* pdf_name,
    199                             int num,
    200                             const void* buffer_void,
    201                             int stride,
    202                             int width,
    203                             int height) {
    204   if (!CheckDimensions(stride, width, height))
    205     return "";
    206 
    207   std::vector<unsigned char> png_encoding;
    208   const unsigned char* buffer = static_cast<const unsigned char*>(buffer_void);
    209   if (!image_diff_png::EncodeBGRAPNG(
    210           buffer, width, height, stride, false, &png_encoding)) {
    211     fprintf(stderr, "Failed to convert bitmap to PNG\n");
    212     return "";
    213   }
    214 
    215   char filename[256];
    216   int chars_formatted = snprintf(
    217       filename, sizeof(filename), "%s.%d.png", pdf_name, num);
    218   if (chars_formatted < 0 ||
    219       static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
    220     fprintf(stderr, "Filename %s is too long\n", filename);
    221     return "";
    222   }
    223 
    224   FILE* fp = fopen(filename, "wb");
    225   if (!fp) {
    226     fprintf(stderr, "Failed to open %s for output\n", filename);
    227     return "";
    228   }
    229 
    230   size_t bytes_written = fwrite(
    231       &png_encoding.front(), 1, png_encoding.size(), fp);
    232   if (bytes_written != png_encoding.size())
    233     fprintf(stderr, "Failed to write to  %s\n", filename);
    234 
    235   (void)fclose(fp);
    236   return std::string(filename);
    237 }
    238 
    239 #ifdef _WIN32
    240 static std::string WriteBmp(const char* pdf_name,
    241                             int num,
    242                             const void* buffer,
    243                             int stride,
    244                             int width,
    245                             int height) {
    246   if (!CheckDimensions(stride, width, height))
    247     return "";
    248 
    249   int out_len = stride * height;
    250   if (out_len > INT_MAX / 3)
    251     return "";
    252 
    253   char filename[256];
    254   snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
    255   FILE* fp = fopen(filename, "wb");
    256   if (!fp)
    257     return "";
    258 
    259   BITMAPINFO bmi = {};
    260   bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
    261   bmi.bmiHeader.biWidth = width;
    262   bmi.bmiHeader.biHeight = -height;  // top-down image
    263   bmi.bmiHeader.biPlanes = 1;
    264   bmi.bmiHeader.biBitCount = 32;
    265   bmi.bmiHeader.biCompression = BI_RGB;
    266   bmi.bmiHeader.biSizeImage = 0;
    267 
    268   BITMAPFILEHEADER file_header = {};
    269   file_header.bfType = 0x4d42;
    270   file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
    271   file_header.bfOffBits = file_header.bfSize - out_len;
    272 
    273   fwrite(&file_header, sizeof(file_header), 1, fp);
    274   fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp);
    275   fwrite(buffer, out_len, 1, fp);
    276   fclose(fp);
    277   return std::string(filename);
    278 }
    279 
    280 void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
    281   int width = static_cast<int>(FPDF_GetPageWidth(page));
    282   int height = static_cast<int>(FPDF_GetPageHeight(page));
    283 
    284   char filename[256];
    285   snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
    286 
    287   HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
    288 
    289   HRGN rgn = CreateRectRgn(0, 0, width, height);
    290   SelectClipRgn(dc, rgn);
    291   DeleteObject(rgn);
    292 
    293   SelectObject(dc, GetStockObject(NULL_PEN));
    294   SelectObject(dc, GetStockObject(WHITE_BRUSH));
    295   // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
    296   Rectangle(dc, 0, 0, width + 1, height + 1);
    297 
    298   FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
    299                   FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
    300 
    301   DeleteEnhMetaFile(CloseEnhMetaFile(dc));
    302 }
    303 #endif
    304 
    305 #ifdef PDF_ENABLE_SKIA
    306 static std::string WriteSkp(const char* pdf_name,
    307                             int num,
    308                             SkPictureRecorder* recorder) {
    309   char filename[256];
    310   int chars_formatted =
    311       snprintf(filename, sizeof(filename), "%s.%d.skp", pdf_name, num);
    312 
    313   if (chars_formatted < 0 ||
    314       static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
    315     fprintf(stderr, "Filename %s is too long\n", filename);
    316     return "";
    317   }
    318 
    319   sk_sp<SkPicture> picture(recorder->finishRecordingAsPicture());
    320   SkFILEWStream wStream(filename);
    321   picture->serialize(&wStream);
    322   return std::string(filename);
    323 }
    324 #endif
    325 
    326 // These example JS platform callback handlers are entirely optional,
    327 // and exist here to show the flow of information from a document back
    328 // to the embedder.
    329 int ExampleAppAlert(IPDF_JSPLATFORM*,
    330                     FPDF_WIDESTRING msg,
    331                     FPDF_WIDESTRING title,
    332                     int type,
    333                     int icon) {
    334   printf("%ls", GetPlatformWString(title).c_str());
    335   if (icon || type)
    336     printf("[icon=%d,type=%d]", icon, type);
    337   printf(": %ls\n", GetPlatformWString(msg).c_str());
    338   return 0;
    339 }
    340 
    341 int ExampleAppResponse(IPDF_JSPLATFORM*,
    342                        FPDF_WIDESTRING question,
    343                        FPDF_WIDESTRING title,
    344                        FPDF_WIDESTRING default_value,
    345                        FPDF_WIDESTRING label,
    346                        FPDF_BOOL is_password,
    347                        void* response,
    348                        int length) {
    349   printf("%ls: %ls, defaultValue=%ls, label=%ls, isPassword=%d, length=%d\n",
    350          GetPlatformWString(title).c_str(),
    351          GetPlatformWString(question).c_str(),
    352          GetPlatformWString(default_value).c_str(),
    353          GetPlatformWString(label).c_str(), is_password, length);
    354 
    355   // UTF-16, always LE regardless of platform.
    356   uint8_t* ptr = static_cast<uint8_t*>(response);
    357   ptr[0] = 'N';
    358   ptr[1] = 0;
    359   ptr[2] = 'o';
    360   ptr[3] = 0;
    361   return 4;
    362 }
    363 
    364 void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
    365   printf("Goto Page: %d\n", page_number);
    366 }
    367 
    368 void ExampleDocMail(IPDF_JSPLATFORM*,
    369                     void* mailData,
    370                     int length,
    371                     FPDF_BOOL UI,
    372                     FPDF_WIDESTRING To,
    373                     FPDF_WIDESTRING Subject,
    374                     FPDF_WIDESTRING CC,
    375                     FPDF_WIDESTRING BCC,
    376                     FPDF_WIDESTRING Msg) {
    377   printf("Mail Msg: %d, to=%ls, cc=%ls, bcc=%ls, subject=%ls, body=%ls\n", UI,
    378          GetPlatformWString(To).c_str(), GetPlatformWString(CC).c_str(),
    379          GetPlatformWString(BCC).c_str(), GetPlatformWString(Subject).c_str(),
    380          GetPlatformWString(Msg).c_str());
    381 }
    382 
    383 void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
    384   std::string feature = "Unknown";
    385   switch (type) {
    386     case FPDF_UNSP_DOC_XFAFORM:
    387       feature = "XFA";
    388       break;
    389     case FPDF_UNSP_DOC_PORTABLECOLLECTION:
    390       feature = "Portfolios_Packages";
    391       break;
    392     case FPDF_UNSP_DOC_ATTACHMENT:
    393     case FPDF_UNSP_ANNOT_ATTACHMENT:
    394       feature = "Attachment";
    395       break;
    396     case FPDF_UNSP_DOC_SECURITY:
    397       feature = "Rights_Management";
    398       break;
    399     case FPDF_UNSP_DOC_SHAREDREVIEW:
    400       feature = "Shared_Review";
    401       break;
    402     case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
    403     case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
    404     case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
    405       feature = "Shared_Form";
    406       break;
    407     case FPDF_UNSP_ANNOT_3DANNOT:
    408       feature = "3D";
    409       break;
    410     case FPDF_UNSP_ANNOT_MOVIE:
    411       feature = "Movie";
    412       break;
    413     case FPDF_UNSP_ANNOT_SOUND:
    414       feature = "Sound";
    415       break;
    416     case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
    417     case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
    418       feature = "Screen";
    419       break;
    420     case FPDF_UNSP_ANNOT_SIG:
    421       feature = "Digital_Signature";
    422       break;
    423   }
    424   printf("Unsupported feature: %s.\n", feature.c_str());
    425 }
    426 
    427 bool ParseCommandLine(const std::vector<std::string>& args,
    428                       Options* options,
    429                       std::vector<std::string>* files) {
    430   if (args.empty())
    431     return false;
    432 
    433   options->exe_path = args[0];
    434   size_t cur_idx = 1;
    435   for (; cur_idx < args.size(); ++cur_idx) {
    436     const std::string& cur_arg = args[cur_idx];
    437     if (cur_arg == "--show-config") {
    438       options->show_config = true;
    439     } else if (cur_arg == "--send-events") {
    440       options->send_events = true;
    441     } else if (cur_arg == "--ppm") {
    442       if (options->output_format != OUTPUT_NONE) {
    443         fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
    444         return false;
    445       }
    446       options->output_format = OUTPUT_PPM;
    447     } else if (cur_arg == "--png") {
    448       if (options->output_format != OUTPUT_NONE) {
    449         fprintf(stderr, "Duplicate or conflicting --png argument\n");
    450         return false;
    451       }
    452       options->output_format = OUTPUT_PNG;
    453     } else if (cur_arg == "--txt") {
    454       if (options->output_format != OUTPUT_NONE) {
    455         fprintf(stderr, "Duplicate or conflicting --txt argument\n");
    456         return false;
    457       }
    458       options->output_format = OUTPUT_TEXT;
    459 #ifdef PDF_ENABLE_SKIA
    460     } else if (cur_arg == "--skp") {
    461       if (options->output_format != OUTPUT_NONE) {
    462         fprintf(stderr, "Duplicate or conflicting --skp argument\n");
    463         return false;
    464       }
    465       options->output_format = OUTPUT_SKP;
    466 #endif
    467     } else if (cur_arg.size() > 11 &&
    468                cur_arg.compare(0, 11, "--font-dir=") == 0) {
    469       if (!options->font_directory.empty()) {
    470         fprintf(stderr, "Duplicate --font-dir argument\n");
    471         return false;
    472       }
    473       options->font_directory = cur_arg.substr(11);
    474 #ifdef _WIN32
    475     } else if (cur_arg == "--emf") {
    476       if (options->output_format != OUTPUT_NONE) {
    477         fprintf(stderr, "Duplicate or conflicting --emf argument\n");
    478         return false;
    479       }
    480       options->output_format = OUTPUT_EMF;
    481     } else if (cur_arg == "--bmp") {
    482       if (options->output_format != OUTPUT_NONE) {
    483         fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
    484         return false;
    485       }
    486       options->output_format = OUTPUT_BMP;
    487 #endif  // _WIN32
    488 
    489 #ifdef PDF_ENABLE_V8
    490 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
    491     } else if (cur_arg.size() > 10 &&
    492                cur_arg.compare(0, 10, "--bin-dir=") == 0) {
    493       if (!options->bin_directory.empty()) {
    494         fprintf(stderr, "Duplicate --bin-dir argument\n");
    495         return false;
    496       }
    497       options->bin_directory = cur_arg.substr(10);
    498 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
    499 #endif  // PDF_ENABLE_V8
    500 
    501     } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--scale=") == 0) {
    502       if (!options->scale_factor_as_string.empty()) {
    503         fprintf(stderr, "Duplicate --scale argument\n");
    504         return false;
    505       }
    506       options->scale_factor_as_string = cur_arg.substr(8);
    507     } else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--pages=") == 0) {
    508       if (options->pages) {
    509         fprintf(stderr, "Duplicate --pages argument\n");
    510         return false;
    511       }
    512       options->pages = true;
    513       const std::string pages_string = cur_arg.substr(8);
    514       size_t first_dash = pages_string.find("-");
    515       if (first_dash == std::string::npos) {
    516         std::stringstream(pages_string) >> options->first_page;
    517         options->last_page = options->first_page;
    518       } else {
    519         std::stringstream(pages_string.substr(0, first_dash)) >>
    520             options->first_page;
    521         std::stringstream(pages_string.substr(first_dash + 1)) >>
    522             options->last_page;
    523       }
    524     } else if (cur_arg == "--md5") {
    525       options->md5 = true;
    526     } else if (cur_arg.size() >= 2 && cur_arg[0] == '-' && cur_arg[1] == '-') {
    527       fprintf(stderr, "Unrecognized argument %s\n", cur_arg.c_str());
    528       return false;
    529     } else {
    530       break;
    531     }
    532   }
    533   for (size_t i = cur_idx; i < args.size(); i++)
    534     files->push_back(args[i]);
    535 
    536   return true;
    537 }
    538 
    539 FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) {
    540   return true;
    541 }
    542 
    543 void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {}
    544 
    545 void SendPageEvents(const FPDF_FORMHANDLE& form,
    546                     const FPDF_PAGE& page,
    547                     const std::string& events) {
    548   auto lines = StringSplit(events, '\n');
    549   for (auto line : lines) {
    550     auto command = StringSplit(line, '#');
    551     if (command[0].empty())
    552       continue;
    553     auto tokens = StringSplit(command[0], ',');
    554     if (tokens[0] == "keycode") {
    555       if (tokens.size() == 2) {
    556         int keycode = atoi(tokens[1].c_str());
    557         FORM_OnKeyDown(form, page, keycode, 0);
    558         FORM_OnKeyUp(form, page, keycode, 0);
    559       } else {
    560         fprintf(stderr, "keycode: bad args\n");
    561       }
    562     } else if (tokens[0] == "mousedown") {
    563       if (tokens.size() == 4) {
    564         int x = atoi(tokens[2].c_str());
    565         int y = atoi(tokens[3].c_str());
    566         if (tokens[1] == "left")
    567           FORM_OnLButtonDown(form, page, 0, x, y);
    568 #ifdef PDF_ENABLE_XFA
    569         else if (tokens[1] == "right")
    570           FORM_OnRButtonDown(form, page, 0, x, y);
    571 #endif
    572         else
    573           fprintf(stderr, "mousedown: bad button name\n");
    574       } else {
    575         fprintf(stderr, "mousedown: bad args\n");
    576       }
    577     } else if (tokens[0] == "mouseup") {
    578       if (tokens.size() == 4) {
    579         int x = atoi(tokens[2].c_str());
    580         int y = atoi(tokens[3].c_str());
    581         if (tokens[1] == "left")
    582           FORM_OnLButtonUp(form, page, 0, x, y);
    583 #ifdef PDF_ENABLE_XFA
    584         else if (tokens[1] == "right")
    585           FORM_OnRButtonUp(form, page, 0, x, y);
    586 #endif
    587         else
    588           fprintf(stderr, "mouseup: bad button name\n");
    589       } else {
    590         fprintf(stderr, "mouseup: bad args\n");
    591       }
    592     } else if (tokens[0] == "mousemove") {
    593       if (tokens.size() == 3) {
    594         int x = atoi(tokens[1].c_str());
    595         int y = atoi(tokens[2].c_str());
    596         FORM_OnMouseMove(form, page, 0, x, y);
    597       } else {
    598         fprintf(stderr, "mousemove: bad args\n");
    599       }
    600     } else {
    601       fprintf(stderr, "Unrecognized event: %s\n", tokens[0].c_str());
    602     }
    603   }
    604 }
    605 
    606 FPDF_PAGE GetPageForIndex(FPDF_FORMFILLINFO* param,
    607                           FPDF_DOCUMENT doc,
    608                           int index) {
    609   FPDF_FORMFILLINFO_PDFiumTest* form_fill_info =
    610       ToPDFiumTestFormFillInfo(param);
    611   auto& loaded_pages = form_fill_info->loaded_pages;
    612 
    613   auto iter = loaded_pages.find(index);
    614   if (iter != loaded_pages.end())
    615     return iter->second;
    616 
    617   FPDF_PAGE page = FPDF_LoadPage(doc, index);
    618   if (!page)
    619     return nullptr;
    620 
    621   FPDF_FORMHANDLE& form_handle = form_fill_info->form_handle;
    622 
    623   FORM_OnAfterLoadPage(page, form_handle);
    624   FORM_DoPageAAction(page, form_handle, FPDFPAGE_AACTION_OPEN);
    625 
    626   loaded_pages[index] = page;
    627   return page;
    628 }
    629 
    630 bool RenderPage(const std::string& name,
    631                 FPDF_DOCUMENT doc,
    632                 FPDF_FORMHANDLE& form,
    633                 FPDF_FORMFILLINFO_PDFiumTest& form_fill_info,
    634                 const int page_index,
    635                 const Options& options,
    636                 const std::string& events) {
    637   FPDF_PAGE page = GetPageForIndex(&form_fill_info, doc, page_index);
    638   if (!page)
    639     return false;
    640 
    641   FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
    642 
    643   if (options.send_events)
    644     SendPageEvents(form, page, events);
    645 
    646   double scale = 1.0;
    647   if (!options.scale_factor_as_string.empty())
    648     std::stringstream(options.scale_factor_as_string) >> scale;
    649 
    650   int width = static_cast<int>(FPDF_GetPageWidth(page) * scale);
    651   int height = static_cast<int>(FPDF_GetPageHeight(page) * scale);
    652   int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
    653   FPDF_BITMAP bitmap = FPDFBitmap_Create(width, height, alpha);
    654   if (bitmap) {
    655     FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
    656     FPDFBitmap_FillRect(bitmap, 0, 0, width, height, fill_color);
    657     FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, 0, FPDF_ANNOT);
    658 
    659     FPDF_FFLDraw(form, bitmap, page, 0, 0, width, height, 0, FPDF_ANNOT);
    660     int stride = FPDFBitmap_GetStride(bitmap);
    661     const char* buffer =
    662         reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap));
    663 
    664     std::string&& image_file_name = "";
    665     switch (options.output_format) {
    666 #ifdef _WIN32
    667       case OUTPUT_BMP:
    668         image_file_name =
    669             WriteBmp(name.c_str(), page_index, buffer, stride, width, height);
    670         break;
    671 
    672       case OUTPUT_EMF:
    673         WriteEmf(page, name.c_str(), page_index);
    674         break;
    675 #endif
    676       case OUTPUT_TEXT:
    677         WriteText(page, name.c_str(), page_index);
    678         break;
    679 
    680       case OUTPUT_PNG:
    681         image_file_name =
    682             WritePng(name.c_str(), page_index, buffer, stride, width, height);
    683         break;
    684 
    685       case OUTPUT_PPM:
    686         image_file_name =
    687             WritePpm(name.c_str(), page_index, buffer, stride, width, height);
    688         break;
    689 
    690 #ifdef PDF_ENABLE_SKIA
    691       case OUTPUT_SKP: {
    692         std::unique_ptr<SkPictureRecorder> recorder(
    693             reinterpret_cast<SkPictureRecorder*>(
    694                 FPDF_RenderPageSkp(page, width, height)));
    695         FPDF_FFLRecord(form, recorder.get(), page, 0, 0, width, height, 0, 0);
    696         image_file_name = WriteSkp(name.c_str(), page_index, recorder.get());
    697       } break;
    698 #endif
    699       default:
    700         break;
    701     }
    702 
    703     // Write the filename and the MD5 of the buffer to stdout if we wrote a
    704     // file.
    705     if (options.md5 && image_file_name != "")
    706       OutputMD5Hash(image_file_name.c_str(), buffer, stride * height);
    707 
    708     FPDFBitmap_Destroy(bitmap);
    709   } else {
    710     fprintf(stderr, "Page was too large to be rendered.\n");
    711   }
    712 
    713   form_fill_info.loaded_pages.erase(page_index);
    714 
    715   FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_CLOSE);
    716   FORM_OnBeforeClosePage(page, form);
    717   FPDFText_ClosePage(text_page);
    718   FPDF_ClosePage(page);
    719   return !!bitmap;
    720 }
    721 
    722 void RenderPdf(const std::string& name,
    723                const char* pBuf,
    724                size_t len,
    725                const Options& options,
    726                const std::string& events) {
    727   IPDF_JSPLATFORM platform_callbacks;
    728   memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
    729   platform_callbacks.version = 3;
    730   platform_callbacks.app_alert = ExampleAppAlert;
    731   platform_callbacks.app_response = ExampleAppResponse;
    732   platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
    733   platform_callbacks.Doc_mail = ExampleDocMail;
    734 
    735   FPDF_FORMFILLINFO_PDFiumTest form_callbacks = {};
    736 #ifdef PDF_ENABLE_XFA
    737   form_callbacks.version = 2;
    738 #else   // PDF_ENABLE_XFA
    739   form_callbacks.version = 1;
    740 #endif  // PDF_ENABLE_XFA
    741   form_callbacks.FFI_GetPage = GetPageForIndex;
    742   form_callbacks.m_pJsPlatform = &platform_callbacks;
    743 
    744   TestLoader loader(pBuf, len);
    745   FPDF_FILEACCESS file_access;
    746   memset(&file_access, '\0', sizeof(file_access));
    747   file_access.m_FileLen = static_cast<unsigned long>(len);
    748   file_access.m_GetBlock = TestLoader::GetBlock;
    749   file_access.m_Param = &loader;
    750 
    751   FX_FILEAVAIL file_avail;
    752   memset(&file_avail, '\0', sizeof(file_avail));
    753   file_avail.version = 1;
    754   file_avail.IsDataAvail = Is_Data_Avail;
    755 
    756   FX_DOWNLOADHINTS hints;
    757   memset(&hints, '\0', sizeof(hints));
    758   hints.version = 1;
    759   hints.AddSegment = Add_Segment;
    760 
    761   FPDF_DOCUMENT doc;
    762   int nRet = PDF_DATA_NOTAVAIL;
    763   bool bIsLinearized = false;
    764   FPDF_AVAIL pdf_avail = FPDFAvail_Create(&file_avail, &file_access);
    765   std::unique_ptr<void, AvailDeleter> scoped_pdf_avail_deleter(pdf_avail);
    766 
    767   if (FPDFAvail_IsLinearized(pdf_avail) == PDF_LINEARIZED) {
    768     doc = FPDFAvail_GetDocument(pdf_avail, nullptr);
    769     if (doc) {
    770       while (nRet == PDF_DATA_NOTAVAIL)
    771         nRet = FPDFAvail_IsDocAvail(pdf_avail, &hints);
    772 
    773       if (nRet == PDF_DATA_ERROR) {
    774         fprintf(stderr, "Unknown error in checking if doc was available.\n");
    775         FPDF_CloseDocument(doc);
    776         return;
    777       }
    778       nRet = FPDFAvail_IsFormAvail(pdf_avail, &hints);
    779       if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL) {
    780         fprintf(stderr,
    781                 "Error %d was returned in checking if form was available.\n",
    782                 nRet);
    783         FPDF_CloseDocument(doc);
    784         return;
    785       }
    786       bIsLinearized = true;
    787     }
    788   } else {
    789     doc = FPDF_LoadCustomDocument(&file_access, nullptr);
    790   }
    791 
    792   if (!doc) {
    793     unsigned long err = FPDF_GetLastError();
    794     fprintf(stderr, "Load pdf docs unsuccessful: ");
    795     switch (err) {
    796       case FPDF_ERR_SUCCESS:
    797         fprintf(stderr, "Success");
    798         break;
    799       case FPDF_ERR_UNKNOWN:
    800         fprintf(stderr, "Unknown error");
    801         break;
    802       case FPDF_ERR_FILE:
    803         fprintf(stderr, "File not found or could not be opened");
    804         break;
    805       case FPDF_ERR_FORMAT:
    806         fprintf(stderr, "File not in PDF format or corrupted");
    807         break;
    808       case FPDF_ERR_PASSWORD:
    809         fprintf(stderr, "Password required or incorrect password");
    810         break;
    811       case FPDF_ERR_SECURITY:
    812         fprintf(stderr, "Unsupported security scheme");
    813         break;
    814       case FPDF_ERR_PAGE:
    815         fprintf(stderr, "Page not found or content error");
    816         break;
    817       default:
    818         fprintf(stderr, "Unknown error %ld", err);
    819     }
    820     fprintf(stderr, ".\n");
    821 
    822     return;
    823   }
    824 
    825   (void)FPDF_GetDocPermissions(doc);
    826 
    827   FPDF_FORMHANDLE form = FPDFDOC_InitFormFillEnvironment(doc, &form_callbacks);
    828   form_callbacks.form_handle = form;
    829 
    830 #ifdef PDF_ENABLE_XFA
    831   int doc_type = DOCTYPE_PDF;
    832   if (FPDF_HasXFAField(doc, &doc_type) && doc_type != DOCTYPE_PDF &&
    833       !FPDF_LoadXFA(doc)) {
    834     fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
    835   }
    836 #endif  // PDF_ENABLE_XFA
    837   FPDF_SetFormFieldHighlightColor(form, 0, 0xFFE4DD);
    838   FPDF_SetFormFieldHighlightAlpha(form, 100);
    839 
    840   FORM_DoDocumentJSAction(form);
    841   FORM_DoDocumentOpenAction(form);
    842 
    843   int page_count = FPDF_GetPageCount(doc);
    844   int rendered_pages = 0;
    845   int bad_pages = 0;
    846   int first_page = options.pages ? options.first_page : 0;
    847   int last_page = options.pages ? options.last_page + 1 : page_count;
    848   for (int i = first_page; i < last_page; ++i) {
    849     if (bIsLinearized) {
    850       nRet = PDF_DATA_NOTAVAIL;
    851       while (nRet == PDF_DATA_NOTAVAIL)
    852         nRet = FPDFAvail_IsPageAvail(pdf_avail, i, &hints);
    853 
    854       if (nRet == PDF_DATA_ERROR) {
    855         fprintf(stderr, "Unknown error in checking if page %d is available.\n",
    856                 i);
    857         FPDFDOC_ExitFormFillEnvironment(form);
    858         FPDF_CloseDocument(doc);
    859         return;
    860       }
    861     }
    862     if (RenderPage(name, doc, form, form_callbacks, i, options, events))
    863       ++rendered_pages;
    864     else
    865       ++bad_pages;
    866   }
    867 
    868   FORM_DoDocumentAAction(form, FPDFDOC_AACTION_WC);
    869 
    870   FPDFDOC_ExitFormFillEnvironment(form);
    871   FPDF_CloseDocument(doc);
    872 
    873   fprintf(stderr, "Rendered %d pages.\n", rendered_pages);
    874   if (bad_pages)
    875     fprintf(stderr, "Skipped %d bad pages.\n", bad_pages);
    876 }
    877 
    878 static void ShowConfig() {
    879   std::string config;
    880   std::string maybe_comma;
    881 #if PDF_ENABLE_V8
    882   config.append(maybe_comma);
    883   config.append("V8");
    884   maybe_comma = ",";
    885 #endif  // PDF_ENABLE_V8
    886 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
    887   config.append(maybe_comma);
    888   config.append("V8_EXTERNAL");
    889   maybe_comma = ",";
    890 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
    891 #ifdef PDF_ENABLE_XFA
    892   config.append(maybe_comma);
    893   config.append("XFA");
    894   maybe_comma = ",";
    895 #endif  // PDF_ENABLE_XFA
    896 #ifdef PDF_ENABLE_ASAN
    897   config.append(maybe_comma);
    898   config.append("ASAN");
    899   maybe_comma = ",";
    900 #endif  // PDF_ENABLE_ASAN
    901   printf("%s\n", config.c_str());
    902 }
    903 
    904 static const char kUsageString[] =
    905     "Usage: pdfium_test [OPTION] [FILE]...\n"
    906     "  --show-config     - print build options and exit\n"
    907     "  --send-events     - send input described by .evt file\n"
    908     "  --bin-dir=<path>  - override path to v8 external data\n"
    909     "  --font-dir=<path> - override path to external fonts\n"
    910     "  --scale=<number>  - scale output size by number (e.g. 0.5)\n"
    911     "  --pages=<number>(-<number>) - only render the given 0-based page(s)\n"
    912 #ifdef _WIN32
    913     "  --bmp - write page images <pdf-name>.<page-number>.bmp\n"
    914     "  --emf - write page meta files <pdf-name>.<page-number>.emf\n"
    915 #endif  // _WIN32
    916     "  --txt - write page text in UTF32-LE <pdf-name>.<page-number>.txt\n"
    917     "  --png - write page images <pdf-name>.<page-number>.png\n"
    918     "  --ppm - write page images <pdf-name>.<page-number>.ppm\n"
    919 #ifdef PDF_ENABLE_SKIA
    920     "  --skp - write page images <pdf-name>.<page-number>.skp\n"
    921 #endif
    922     "  --md5 - write output image paths and their md5 hashes to stdout.\n"
    923     "";
    924 
    925 int main(int argc, const char* argv[]) {
    926   std::vector<std::string> args(argv, argv + argc);
    927   Options options;
    928   std::vector<std::string> files;
    929   if (!ParseCommandLine(args, &options, &files)) {
    930     fprintf(stderr, "%s", kUsageString);
    931     return 1;
    932   }
    933 
    934   if (options.show_config) {
    935     ShowConfig();
    936     return 0;
    937   }
    938 
    939   if (files.empty()) {
    940     fprintf(stderr, "No input files.\n");
    941     return 1;
    942   }
    943 
    944 #ifdef PDF_ENABLE_V8
    945   v8::Platform* platform;
    946 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
    947   v8::StartupData natives;
    948   v8::StartupData snapshot;
    949   InitializeV8ForPDFium(options.exe_path, options.bin_directory, &natives,
    950                         &snapshot, &platform);
    951 #else   // V8_USE_EXTERNAL_STARTUP_DATA
    952   InitializeV8ForPDFium(options.exe_path, &platform);
    953 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
    954 #endif  // PDF_ENABLE_V8
    955 
    956   FPDF_LIBRARY_CONFIG config;
    957   config.version = 2;
    958   config.m_pUserFontPaths = nullptr;
    959   config.m_pIsolate = nullptr;
    960   config.m_v8EmbedderSlot = 0;
    961 
    962   const char* path_array[2];
    963   if (!options.font_directory.empty()) {
    964     path_array[0] = options.font_directory.c_str();
    965     path_array[1] = nullptr;
    966     config.m_pUserFontPaths = path_array;
    967   }
    968   FPDF_InitLibraryWithConfig(&config);
    969 
    970   UNSUPPORT_INFO unsupported_info;
    971   memset(&unsupported_info, '\0', sizeof(unsupported_info));
    972   unsupported_info.version = 1;
    973   unsupported_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
    974 
    975   FSDK_SetUnSpObjProcessHandler(&unsupported_info);
    976 
    977   for (const std::string& filename : files) {
    978     size_t file_length = 0;
    979     std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
    980         GetFileContents(filename.c_str(), &file_length);
    981     if (!file_contents)
    982       continue;
    983     fprintf(stderr, "Rendering PDF file %s.\n", filename.c_str());
    984     std::string events;
    985     if (options.send_events) {
    986       std::string event_filename = filename;
    987       size_t event_length = 0;
    988       size_t extension_pos = event_filename.find(".pdf");
    989       if (extension_pos != std::string::npos) {
    990         event_filename.replace(extension_pos, 4, ".evt");
    991         if (access(event_filename.c_str(), R_OK) == 0) {
    992           fprintf(stderr, "Using event file %s.\n", event_filename.c_str());
    993           std::unique_ptr<char, pdfium::FreeDeleter> event_contents =
    994               GetFileContents(event_filename.c_str(), &event_length);
    995           if (event_contents) {
    996             fprintf(stderr, "Sending events from: %s\n",
    997                     event_filename.c_str());
    998             events = std::string(event_contents.get(), event_length);
    999           }
   1000         }
   1001       }
   1002     }
   1003     RenderPdf(filename, file_contents.get(), file_length, options, events);
   1004   }
   1005 
   1006   FPDF_DestroyLibrary();
   1007 #ifdef PDF_ENABLE_V8
   1008   v8::V8::ShutdownPlatform();
   1009   delete platform;
   1010 
   1011 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
   1012   free(const_cast<char*>(natives.data));
   1013   free(const_cast<char*>(snapshot.data));
   1014 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
   1015 #endif  // PDF_ENABLE_V8
   1016 
   1017   return 0;
   1018 }
   1019