pdfium_test.cc revision 33357cad1fd1321a2b38d2963e2585f27ce980a2
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 52enum 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 66struct 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 88struct 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 98struct AvailDeleter { 99 inline void operator()(FPDF_AVAIL avail) const { FPDFAvail_Destroy(avail); } 100}; 101 102static FPDF_FORMFILLINFO_PDFiumTest* ToPDFiumTestFormFillInfo( 103 FPDF_FORMFILLINFO* form_fill_info) { 104 return static_cast<FPDF_FORMFILLINFO_PDFiumTest*>(form_fill_info); 105} 106 107static 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 115static 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 125static 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 167void 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 198static 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 240static 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 280void 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 306static 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. 329int 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 341int 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 364void ExampleDocGotoPage(IPDF_JSPLATFORM*, int page_number) { 365 printf("Goto Page: %d\n", page_number); 366} 367 368void 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 383void 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 427bool 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 539FPDF_BOOL Is_Data_Avail(FX_FILEAVAIL* avail, size_t offset, size_t size) { 540 return true; 541} 542 543void Add_Segment(FX_DOWNLOADHINTS* hints, size_t offset, size_t size) {} 544 545void 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 606FPDF_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 630bool 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 722void 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 878static 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 904static 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 925int 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