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