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 <list>
     11 #include <sstream>
     12 #include <string>
     13 #include <utility>
     14 #include <vector>
     15 
     16 #include "image_diff_png.h"
     17 #include "public/fpdf_dataavail.h"
     18 #include "public/fpdf_edit.h"
     19 #include "public/fpdf_ext.h"
     20 #include "public/fpdf_formfill.h"
     21 #include "public/fpdf_text.h"
     22 #include "public/fpdfview.h"
     23 #include "testing/test_support.h"
     24 
     25 #ifdef PDF_ENABLE_V8
     26 #include "v8/include/libplatform/libplatform.h"
     27 #include "v8/include/v8.h"
     28 #endif  // PDF_ENABLE_V8
     29 
     30 #ifdef _WIN32
     31 #define snprintf _snprintf
     32 #endif
     33 
     34 enum OutputFormat {
     35   OUTPUT_NONE,
     36   OUTPUT_PPM,
     37   OUTPUT_PNG,
     38 #ifdef _WIN32
     39   OUTPUT_BMP,
     40   OUTPUT_EMF,
     41 #endif
     42 };
     43 
     44 struct Options {
     45   Options() : output_format(OUTPUT_NONE) { }
     46 
     47   OutputFormat output_format;
     48   std::string scale_factor_as_string;
     49   std::string exe_path;
     50   std::string bin_directory;
     51   std::string font_directory;
     52 };
     53 
     54 static bool CheckDimensions(int stride, int width, int height) {
     55   if (stride < 0 || width < 0 || height < 0)
     56     return false;
     57   if (height > 0 && width > INT_MAX / height)
     58     return false;
     59   return true;
     60 }
     61 
     62 static void WritePpm(const char* pdf_name, int num, const void* buffer_void,
     63                      int stride, int width, int height) {
     64   const char* buffer = reinterpret_cast<const char*>(buffer_void);
     65 
     66   if (!CheckDimensions(stride, width, height))
     67     return;
     68 
     69   int out_len = width * height;
     70   if (out_len > INT_MAX / 3)
     71     return;
     72   out_len *= 3;
     73 
     74   char filename[256];
     75   snprintf(filename, sizeof(filename), "%s.%d.ppm", pdf_name, num);
     76   FILE* fp = fopen(filename, "wb");
     77   if (!fp)
     78     return;
     79   fprintf(fp, "P6\n# PDF test render\n%d %d\n255\n", width, height);
     80   // Source data is B, G, R, unused.
     81   // Dest data is R, G, B.
     82   char* result = new char[out_len];
     83   for (int h = 0; h < height; ++h) {
     84     const char* src_line = buffer + (stride * h);
     85     char* dest_line = result + (width * h * 3);
     86     for (int w = 0; w < width; ++w) {
     87       // R
     88       dest_line[w * 3] = src_line[(w * 4) + 2];
     89       // G
     90       dest_line[(w * 3) + 1] = src_line[(w * 4) + 1];
     91       // B
     92       dest_line[(w * 3) + 2] = src_line[w * 4];
     93     }
     94   }
     95   fwrite(result, out_len, 1, fp);
     96   delete[] result;
     97   fclose(fp);
     98 }
     99 
    100 static void WritePng(const char* pdf_name, int num, const void* buffer_void,
    101                      int stride, int width, int height) {
    102   if (!CheckDimensions(stride, width, height))
    103     return;
    104 
    105   std::vector<unsigned char> png_encoding;
    106   const unsigned char* buffer = static_cast<const unsigned char*>(buffer_void);
    107   if (!image_diff_png::EncodeBGRAPNG(
    108           buffer, width, height, stride, false, &png_encoding)) {
    109     fprintf(stderr, "Failed to convert bitmap to PNG\n");
    110     return;
    111   }
    112 
    113   char filename[256];
    114   int chars_formatted = snprintf(
    115       filename, sizeof(filename), "%s.%d.png", pdf_name, num);
    116   if (chars_formatted < 0 ||
    117       static_cast<size_t>(chars_formatted) >= sizeof(filename)) {
    118     fprintf(stderr, "Filname %s is too long\n", filename);
    119     return;
    120   }
    121 
    122   FILE* fp = fopen(filename, "wb");
    123   if (!fp) {
    124     fprintf(stderr, "Failed to open %s for output\n", filename);
    125     return;
    126   }
    127 
    128   size_t bytes_written = fwrite(
    129       &png_encoding.front(), 1, png_encoding.size(), fp);
    130   if (bytes_written != png_encoding.size())
    131     fprintf(stderr, "Failed to write to  %s\n", filename);
    132 
    133   (void)fclose(fp);
    134 }
    135 
    136 #ifdef _WIN32
    137 static void WriteBmp(const char* pdf_name, int num, const void* buffer,
    138                      int stride, int width, int height) {
    139   if (!CheckDimensions(stride, width, height))
    140     return;
    141 
    142   int out_len = stride * height;
    143   if (out_len > INT_MAX / 3)
    144     return;
    145 
    146   char filename[256];
    147   snprintf(filename, sizeof(filename), "%s.%d.bmp", pdf_name, num);
    148   FILE* fp = fopen(filename, "wb");
    149   if (!fp)
    150     return;
    151 
    152   BITMAPINFO bmi = {};
    153   bmi.bmiHeader.biSize = sizeof(bmi) - sizeof(RGBQUAD);
    154   bmi.bmiHeader.biWidth = width;
    155   bmi.bmiHeader.biHeight = -height;  // top-down image
    156   bmi.bmiHeader.biPlanes = 1;
    157   bmi.bmiHeader.biBitCount = 32;
    158   bmi.bmiHeader.biCompression = BI_RGB;
    159   bmi.bmiHeader.biSizeImage = 0;
    160 
    161   BITMAPFILEHEADER file_header = {};
    162   file_header.bfType = 0x4d42;
    163   file_header.bfSize = sizeof(file_header) + bmi.bmiHeader.biSize + out_len;
    164   file_header.bfOffBits = file_header.bfSize - out_len;
    165 
    166   fwrite(&file_header, sizeof(file_header), 1, fp);
    167   fwrite(&bmi, bmi.bmiHeader.biSize, 1, fp);
    168   fwrite(buffer, out_len, 1, fp);
    169   fclose(fp);
    170 }
    171 
    172 void WriteEmf(FPDF_PAGE page, const char* pdf_name, int num) {
    173   int width = static_cast<int>(FPDF_GetPageWidth(page));
    174   int height = static_cast<int>(FPDF_GetPageHeight(page));
    175 
    176   char filename[256];
    177   snprintf(filename, sizeof(filename), "%s.%d.emf", pdf_name, num);
    178 
    179   HDC dc = CreateEnhMetaFileA(nullptr, filename, nullptr, nullptr);
    180 
    181   HRGN rgn = CreateRectRgn(0, 0, width, height);
    182   SelectClipRgn(dc, rgn);
    183   DeleteObject(rgn);
    184 
    185   SelectObject(dc, GetStockObject(NULL_PEN));
    186   SelectObject(dc, GetStockObject(WHITE_BRUSH));
    187   // If a PS_NULL pen is used, the dimensions of the rectangle are 1 pixel less.
    188   Rectangle(dc, 0, 0, width + 1, height + 1);
    189 
    190   FPDF_RenderPage(dc, page, 0, 0, width, height, 0,
    191                   FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH);
    192 
    193   DeleteEnhMetaFile(CloseEnhMetaFile(dc));
    194 }
    195 #endif
    196 
    197 int ExampleAppAlert(IPDF_JSPLATFORM*, FPDF_WIDESTRING msg, FPDF_WIDESTRING,
    198                     int, int) {
    199   std::wstring platform_string = GetPlatformWString(msg);
    200   printf("Alert: %ls\n", platform_string.c_str());
    201   return 0;
    202 }
    203 
    204 void ExampleDocGotoPage(IPDF_JSPLATFORM*, int pageNumber) {
    205   printf("Goto Page: %d\n", pageNumber);
    206 }
    207 
    208 void ExampleUnsupportedHandler(UNSUPPORT_INFO*, int type) {
    209   std::string feature = "Unknown";
    210   switch (type) {
    211     case FPDF_UNSP_DOC_XFAFORM:
    212       feature = "XFA";
    213       break;
    214     case FPDF_UNSP_DOC_PORTABLECOLLECTION:
    215       feature = "Portfolios_Packages";
    216       break;
    217     case FPDF_UNSP_DOC_ATTACHMENT:
    218     case FPDF_UNSP_ANNOT_ATTACHMENT:
    219       feature = "Attachment";
    220       break;
    221     case FPDF_UNSP_DOC_SECURITY:
    222       feature = "Rights_Management";
    223       break;
    224     case FPDF_UNSP_DOC_SHAREDREVIEW:
    225       feature = "Shared_Review";
    226       break;
    227     case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT:
    228     case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM:
    229     case FPDF_UNSP_DOC_SHAREDFORM_EMAIL:
    230       feature = "Shared_Form";
    231       break;
    232     case FPDF_UNSP_ANNOT_3DANNOT:
    233       feature = "3D";
    234       break;
    235     case FPDF_UNSP_ANNOT_MOVIE:
    236       feature = "Movie";
    237       break;
    238     case FPDF_UNSP_ANNOT_SOUND:
    239       feature = "Sound";
    240       break;
    241     case FPDF_UNSP_ANNOT_SCREEN_MEDIA:
    242     case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA:
    243       feature = "Screen";
    244       break;
    245     case FPDF_UNSP_ANNOT_SIG:
    246       feature = "Digital_Signature";
    247       break;
    248   }
    249   printf("Unsupported feature: %s.\n", feature.c_str());
    250 }
    251 
    252 bool ParseCommandLine(const std::vector<std::string>& args,
    253                       Options* options, std::list<std::string>* files) {
    254   if (args.empty()) {
    255     return false;
    256   }
    257   options->exe_path = args[0];
    258   size_t cur_idx = 1;
    259   for (; cur_idx < args.size(); ++cur_idx) {
    260     const std::string& cur_arg = args[cur_idx];
    261     if (cur_arg == "--ppm") {
    262       if (options->output_format != OUTPUT_NONE) {
    263         fprintf(stderr, "Duplicate or conflicting --ppm argument\n");
    264         return false;
    265       }
    266       options->output_format = OUTPUT_PPM;
    267     } else if (cur_arg == "--png") {
    268       if (options->output_format != OUTPUT_NONE) {
    269         fprintf(stderr, "Duplicate or conflicting --png argument\n");
    270         return false;
    271       }
    272       options->output_format = OUTPUT_PNG;
    273     } else if (cur_arg.size() > 11 &&
    274                cur_arg.compare(0, 11, "--font-dir=") == 0) {
    275       if (!options->font_directory.empty()) {
    276         fprintf(stderr, "Duplicate --font-dir argument\n");
    277         return false;
    278       }
    279       options->font_directory = cur_arg.substr(11);
    280     }
    281 #ifdef _WIN32
    282     else if (cur_arg == "--emf") {
    283       if (options->output_format != OUTPUT_NONE) {
    284         fprintf(stderr, "Duplicate or conflicting --emf argument\n");
    285         return false;
    286       }
    287       options->output_format = OUTPUT_EMF;
    288     }
    289     else if (cur_arg == "--bmp") {
    290       if (options->output_format != OUTPUT_NONE) {
    291         fprintf(stderr, "Duplicate or conflicting --bmp argument\n");
    292         return false;
    293       }
    294       options->output_format = OUTPUT_BMP;
    295     }
    296 #endif  // _WIN32
    297 #ifdef PDF_ENABLE_V8
    298 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
    299     else if (cur_arg.size() > 10 && cur_arg.compare(0, 10, "--bin-dir=") == 0) {
    300       if (!options->bin_directory.empty()) {
    301         fprintf(stderr, "Duplicate --bin-dir argument\n");
    302         return false;
    303       }
    304       options->bin_directory = cur_arg.substr(10);
    305     }
    306 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
    307 #endif  // PDF_ENABLE_V8
    308     else if (cur_arg.size() > 8 && cur_arg.compare(0, 8, "--scale=") == 0) {
    309       if (!options->scale_factor_as_string.empty()) {
    310         fprintf(stderr, "Duplicate --scale argument\n");
    311         return false;
    312       }
    313       options->scale_factor_as_string = cur_arg.substr(8);
    314     }
    315     else
    316       break;
    317   }
    318   if (cur_idx >= args.size()) {
    319     fprintf(stderr, "No input files.\n");
    320     return false;
    321   }
    322   for (size_t i = cur_idx; i < args.size(); i++) {
    323     files->push_back(args[i]);
    324   }
    325   return true;
    326 }
    327 
    328 FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* pThis, size_t offset, size_t size) {
    329   return true;
    330 }
    331 
    332 void Add_Segment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
    333 }
    334 
    335 bool RenderPage(const std::string& name,
    336                 const FPDF_DOCUMENT& doc,
    337                 const FPDF_FORMHANDLE& form,
    338                 const int page_index,
    339                 const Options& options) {
    340   FPDF_PAGE page = FPDF_LoadPage(doc, page_index);
    341   if (!page) {
    342     return false;
    343   }
    344   FPDF_TEXTPAGE text_page = FPDFText_LoadPage(page);
    345   FORM_OnAfterLoadPage(page, form);
    346   FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_OPEN);
    347 
    348   double scale = 1.0;
    349   if (!options.scale_factor_as_string.empty()) {
    350     std::stringstream(options.scale_factor_as_string) >> scale;
    351   }
    352   int width = static_cast<int>(FPDF_GetPageWidth(page) * scale);
    353   int height = static_cast<int>(FPDF_GetPageHeight(page) * scale);
    354   int alpha = FPDFPage_HasTransparency(page) ? 1 : 0;
    355   FPDF_BITMAP bitmap = FPDFBitmap_Create(width, height, alpha);
    356   if (!bitmap) {
    357     fprintf(stderr, "Page was too large to be rendered.\n");
    358     return false;
    359   }
    360   FPDF_DWORD fill_color = alpha ? 0x00000000 : 0xFFFFFFFF;
    361   FPDFBitmap_FillRect(bitmap, 0, 0, width, height, fill_color);
    362   FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, 0, 0);
    363 
    364   FPDF_FFLDraw(form, bitmap, page, 0, 0, width, height, 0, 0);
    365   int stride = FPDFBitmap_GetStride(bitmap);
    366   const char* buffer =
    367       reinterpret_cast<const char*>(FPDFBitmap_GetBuffer(bitmap));
    368 
    369   switch (options.output_format) {
    370 #ifdef _WIN32
    371     case OUTPUT_BMP:
    372       WriteBmp(name.c_str(), page_index, buffer, stride, width, height);
    373       break;
    374 
    375     case OUTPUT_EMF:
    376       WriteEmf(page, name.c_str(), page_index);
    377       break;
    378 #endif
    379     case OUTPUT_PNG:
    380       WritePng(name.c_str(), page_index, buffer, stride, width, height);
    381       break;
    382 
    383     case OUTPUT_PPM:
    384       WritePpm(name.c_str(), page_index, buffer, stride, width, height);
    385       break;
    386 
    387     default:
    388       break;
    389   }
    390 
    391   FPDFBitmap_Destroy(bitmap);
    392   FORM_DoPageAAction(page, form, FPDFPAGE_AACTION_CLOSE);
    393   FORM_OnBeforeClosePage(page, form);
    394   FPDFText_ClosePage(text_page);
    395   FPDF_ClosePage(page);
    396   return true;
    397 }
    398 
    399 void RenderPdf(const std::string& name, const char* pBuf, size_t len,
    400                const Options& options) {
    401   fprintf(stderr, "Rendering PDF file %s.\n", name.c_str());
    402 
    403   IPDF_JSPLATFORM platform_callbacks;
    404   memset(&platform_callbacks, '\0', sizeof(platform_callbacks));
    405   platform_callbacks.version = 3;
    406   platform_callbacks.app_alert = ExampleAppAlert;
    407   platform_callbacks.Doc_gotoPage = ExampleDocGotoPage;
    408 
    409   FPDF_FORMFILLINFO form_callbacks;
    410   memset(&form_callbacks, '\0', sizeof(form_callbacks));
    411 #ifdef PDF_ENABLE_XFA
    412   form_callbacks.version = 2;
    413 #else   // PDF_ENABLE_XFA
    414   form_callbacks.version = 1;
    415 #endif  // PDF_ENABLE_XFA
    416   form_callbacks.m_pJsPlatform = &platform_callbacks;
    417 
    418   TestLoader loader(pBuf, len);
    419   FPDF_FILEACCESS file_access;
    420   memset(&file_access, '\0', sizeof(file_access));
    421   file_access.m_FileLen = static_cast<unsigned long>(len);
    422   file_access.m_GetBlock = TestLoader::GetBlock;
    423   file_access.m_Param = &loader;
    424 
    425   FX_FILEAVAIL file_avail;
    426   memset(&file_avail, '\0', sizeof(file_avail));
    427   file_avail.version = 1;
    428   file_avail.IsDataAvail = Is_Data_Avail;
    429 
    430   FX_DOWNLOADHINTS hints;
    431   memset(&hints, '\0', sizeof(hints));
    432   hints.version = 1;
    433   hints.AddSegment = Add_Segment;
    434 
    435   FPDF_DOCUMENT doc;
    436   int nRet = PDF_DATA_NOTAVAIL;
    437   bool bIsLinearized = false;
    438   FPDF_AVAIL pdf_avail = FPDFAvail_Create(&file_avail, &file_access);
    439 
    440   if (FPDFAvail_IsLinearized(pdf_avail) == PDF_LINEARIZED) {
    441     fprintf(stderr, "Linearized path...\n");
    442     doc = FPDFAvail_GetDocument(pdf_avail, nullptr);
    443     if (doc) {
    444       while (nRet == PDF_DATA_NOTAVAIL) {
    445         nRet = FPDFAvail_IsDocAvail(pdf_avail, &hints);
    446       }
    447       if (nRet == PDF_DATA_ERROR) {
    448         fprintf(stderr, "Unknown error in checking if doc was available.\n");
    449         return;
    450       }
    451       nRet = FPDFAvail_IsFormAvail(pdf_avail, &hints);
    452       if (nRet == PDF_FORM_ERROR || nRet == PDF_FORM_NOTAVAIL) {
    453         fprintf(stderr,
    454                 "Error %d was returned in checking if form was available.\n",
    455                 nRet);
    456         return;
    457       }
    458       bIsLinearized = true;
    459     }
    460   } else {
    461     fprintf(stderr, "Non-linearized path...\n");
    462     doc = FPDF_LoadCustomDocument(&file_access, nullptr);
    463   }
    464 
    465   if (!doc) {
    466     unsigned long err = FPDF_GetLastError();
    467     fprintf(stderr, "Load pdf docs unsuccessful: ");
    468     switch (err) {
    469       case FPDF_ERR_SUCCESS:
    470         fprintf(stderr, "Success");
    471         break;
    472       case FPDF_ERR_UNKNOWN:
    473         fprintf(stderr, "Unknown error");
    474         break;
    475       case FPDF_ERR_FILE:
    476         fprintf(stderr, "File not found or could not be opened");
    477         break;
    478       case FPDF_ERR_FORMAT:
    479         fprintf(stderr, "File not in PDF format or corrupted");
    480         break;
    481       case FPDF_ERR_PASSWORD:
    482         fprintf(stderr, "Password required or incorrect password");
    483         break;
    484       case FPDF_ERR_SECURITY:
    485         fprintf(stderr, "Unsupported security scheme");
    486         break;
    487       case FPDF_ERR_PAGE:
    488         fprintf(stderr, "Page not found or content error");
    489         break;
    490       default:
    491         fprintf(stderr, "Unknown error %ld", err);
    492     }
    493     fprintf(stderr, ".\n");
    494 
    495     FPDFAvail_Destroy(pdf_avail);
    496     return;
    497   }
    498 
    499   (void)FPDF_GetDocPermissions(doc);
    500 
    501   FPDF_FORMHANDLE form = FPDFDOC_InitFormFillEnvironment(doc, &form_callbacks);
    502 #ifdef PDF_ENABLE_XFA
    503   int docType = DOCTYPE_PDF;
    504   if (FPDF_HasXFAField(doc, &docType) && docType != DOCTYPE_PDF &&
    505       !FPDF_LoadXFA(doc)) {
    506     fprintf(stderr, "LoadXFA unsuccessful, continuing anyway.\n");
    507   }
    508 #endif  // PDF_ENABLE_XFA
    509   FPDF_SetFormFieldHighlightColor(form, 0, 0xFFE4DD);
    510   FPDF_SetFormFieldHighlightAlpha(form, 100);
    511 
    512   FORM_DoDocumentJSAction(form);
    513   FORM_DoDocumentOpenAction(form);
    514 
    515   int page_count = FPDF_GetPageCount(doc);
    516   int rendered_pages = 0;
    517   int bad_pages = 0;
    518   for (int i = 0; i < page_count; ++i) {
    519     if (bIsLinearized) {
    520       nRet = PDF_DATA_NOTAVAIL;
    521       while (nRet == PDF_DATA_NOTAVAIL) {
    522         nRet = FPDFAvail_IsPageAvail(pdf_avail, i, &hints);
    523       }
    524       if (nRet == PDF_DATA_ERROR) {
    525         fprintf(stderr, "Unknown error in checking if page %d is available.\n",
    526                 i);
    527         return;
    528       }
    529     }
    530     if (RenderPage(name, doc, form, i, options)) {
    531       ++rendered_pages;
    532     } else {
    533       ++bad_pages;
    534     }
    535   }
    536 
    537   FORM_DoDocumentAAction(form, FPDFDOC_AACTION_WC);
    538 
    539 #ifdef PDF_ENABLE_XFA
    540   // Note: The shut down order here is the reverse of the non-XFA branch order.
    541   // Need to work out if this is required, and if it is, the lifetimes of
    542   // objects owned by |doc| that |form| reference.
    543   FPDF_CloseDocument(doc);
    544   FPDFDOC_ExitFormFillEnvironment(form);
    545 #else  // PDF_ENABLE_XFA
    546   FPDFDOC_ExitFormFillEnvironment(form);
    547   FPDF_CloseDocument(doc);
    548 #endif  // PDF_ENABLE_XFA
    549 
    550   FPDFAvail_Destroy(pdf_avail);
    551 
    552   fprintf(stderr, "Rendered %d pages.\n", rendered_pages);
    553   fprintf(stderr, "Skipped %d bad pages.\n", bad_pages);
    554 }
    555 
    556 static const char usage_string[] =
    557     "Usage: pdfium_test [OPTION] [FILE]...\n"
    558     "  --bin-dir=<path>  - override path to v8 external data\n"
    559     "  --font-dir=<path> - override path to external fonts\n"
    560     "  --scale=<number>  - scale output size by number (e.g. 0.5)\n"
    561 #ifdef _WIN32
    562     "  --bmp - write page images <pdf-name>.<page-number>.bmp\n"
    563     "  --emf - write page meta files <pdf-name>.<page-number>.emf\n"
    564 #endif  // _WIN32
    565     "  --png - write page images <pdf-name>.<page-number>.png\n"
    566     "  --ppm - write page images <pdf-name>.<page-number>.ppm\n";
    567 
    568 int main(int argc, const char* argv[]) {
    569   std::vector<std::string> args(argv, argv + argc);
    570   Options options;
    571   std::list<std::string> files;
    572   if (!ParseCommandLine(args, &options, &files)) {
    573     fprintf(stderr, "%s", usage_string);
    574     return 1;
    575   }
    576 
    577 #ifdef PDF_ENABLE_V8
    578   v8::Platform* platform;
    579 #ifdef V8_USE_EXTERNAL_STARTUP_DATA
    580   v8::StartupData natives;
    581   v8::StartupData snapshot;
    582   InitializeV8ForPDFium(options.exe_path, options.bin_directory, &natives,
    583                         &snapshot, &platform);
    584 #else   // V8_USE_EXTERNAL_STARTUP_DATA
    585   InitializeV8ForPDFium(&platform);
    586 #endif  // V8_USE_EXTERNAL_STARTUP_DATA
    587 #endif  // PDF_ENABLE_V8
    588 
    589   FPDF_LIBRARY_CONFIG config;
    590   config.version = 2;
    591   config.m_pUserFontPaths = nullptr;
    592   config.m_pIsolate = nullptr;
    593   config.m_v8EmbedderSlot = 0;
    594 
    595   const char* path_array[2];
    596   if (!options.font_directory.empty()) {
    597     path_array[0] = options.font_directory.c_str();
    598     path_array[1] = nullptr;
    599     config.m_pUserFontPaths = path_array;
    600   }
    601   FPDF_InitLibraryWithConfig(&config);
    602 
    603   UNSUPPORT_INFO unsuppored_info;
    604   memset(&unsuppored_info, '\0', sizeof(unsuppored_info));
    605   unsuppored_info.version = 1;
    606   unsuppored_info.FSDK_UnSupport_Handler = ExampleUnsupportedHandler;
    607 
    608   FSDK_SetUnSpObjProcessHandler(&unsuppored_info);
    609 
    610   while (!files.empty()) {
    611     std::string filename = files.front();
    612     files.pop_front();
    613     size_t file_length = 0;
    614     std::unique_ptr<char, pdfium::FreeDeleter> file_contents =
    615         GetFileContents(filename.c_str(), &file_length);
    616     if (file_contents)
    617       RenderPdf(filename, file_contents.get(), file_length, options);
    618   }
    619 
    620   FPDF_DestroyLibrary();
    621 #ifdef PDF_ENABLE_V8
    622   v8::V8::ShutdownPlatform();
    623   delete platform;
    624 #endif  // PDF_ENABLE_V8
    625 
    626   return 0;
    627 }
    628