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 {
OptionsOptions67   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 {
operator ()AvailDeleter99   inline void operator()(FPDF_AVAIL avail) const { FPDFAvail_Destroy(avail); }
100 };
101 
ToPDFiumTestFormFillInfo(FPDF_FORMFILLINFO * form_fill_info)102 static FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo(
103     FPDF_FORMFILLINFO* form_fill_info) {
104   return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info);
105 }
106 
CheckDimensions(int stride,int width,int height)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 
OutputMD5Hash(const char * file_name,const char * buffer,int len)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 
WritePpm(const char * pdf_name,int num,const void * buffer_void,int stride,int width,int height)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 
WriteText(FPDF_PAGE page,const char * pdf_name,int num)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 
WritePng(const char * pdf_name,int num,const void * buffer_void,int stride,int width,int height)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
WriteBmp(const char * pdf_name,int num,const void * buffer,int stride,int width,int height)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 
WriteEmf(FPDF_PAGE page,const char * pdf_name,int num)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
WriteSkp(const char * pdf_name,int num,SkPictureRecorder * recorder)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.
ExampleAppAlert(IPDF_JSPLATFORM *,FPDF_WIDESTRING msg,FPDF_WIDESTRING title,int type,int icon)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 
ExampleAppResponse(IPDF_JSPLATFORM *,FPDF_WIDESTRING question,FPDF_WIDESTRING title,FPDF_WIDESTRING default_value,FPDF_WIDESTRING label,FPDF_BOOL is_password,void * response,int length)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 
ExampleDocGotoPage(IPDF_JSPLATFORM *,int page_number)364 void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) {
365   printf("Goto Page: %d\n", page_number);
366 }
367 
ExampleDocMail(IPDF_JSPLATFORM *,void * mailData,int length,FPDF_BOOL UI,FPDF_WIDESTRING To,FPDF_WIDESTRING Subject,FPDF_WIDESTRING CC,FPDF_WIDESTRING BCC,FPDF_WIDESTRING Msg)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 
ExampleUnsupportedHandler(UNSUPPORT_INFO *,int type)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 
ParseCommandLine(const std::vector<std::string> & args,Options * options,std::vector<std::string> * files)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 
Is_Data_Avail(FX_FILEAVAIL * avail,size_t offset,size_t size)539 FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) {
540   return true;
541 }
542 
Add_Segment(FX_DOWNLOADHINTS * hints,size_t offset,size_t size)543 void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {}
544 
SendPageEvents(const FPDF_FORMHANDLE & form,const FPDF_PAGE & page,const std::string & events)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 
GetPageForIndex(FPDF_FORMFILLINFO * param,FPDF_DOCUMENT doc,int index)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 
RenderPage(const std::string & name,FPDF_DOCUMENT doc,FPDF_FORMHANDLE & form,FPDF_FORMFILLINFO_PDFiumTest & form_fill_info,const int page_index,const Options & options,const std::string & events)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 
RenderPdf(const std::string & name,const char * pBuf,size_t len,const Options & options,const std::string & events)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 
ShowConfig()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 
main(int argc,const char * argv[])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