1 // Copyright (c) 2012 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 "pdf/pdfium/pdfium_engine.h" 6 7 #include <math.h> 8 9 #include "base/json/json_writer.h" 10 #include "base/logging.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/stl_util.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/strings/string_piece.h" 15 #include "base/strings/string_util.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "base/values.h" 18 #include "pdf/draw_utils.h" 19 #include "pdf/pdfium/pdfium_mem_buffer_file_read.h" 20 #include "pdf/pdfium/pdfium_mem_buffer_file_write.h" 21 #include "ppapi/c/pp_errors.h" 22 #include "ppapi/c/pp_input_event.h" 23 #include "ppapi/c/ppb_core.h" 24 #include "ppapi/c/private/ppb_pdf.h" 25 #include "ppapi/cpp/dev/memory_dev.h" 26 #include "ppapi/cpp/input_event.h" 27 #include "ppapi/cpp/instance.h" 28 #include "ppapi/cpp/module.h" 29 #include "ppapi/cpp/private/pdf.h" 30 #include "ppapi/cpp/trusted/browser_font_trusted.h" 31 #include "ppapi/cpp/url_response_info.h" 32 #include "ppapi/cpp/var.h" 33 #include "third_party/pdfium/fpdfsdk/include/fpdf_ext.h" 34 #include "third_party/pdfium/fpdfsdk/include/fpdf_flatten.h" 35 #include "third_party/pdfium/fpdfsdk/include/fpdf_searchex.h" 36 #include "third_party/pdfium/fpdfsdk/include/fpdf_sysfontinfo.h" 37 #include "third_party/pdfium/fpdfsdk/include/fpdf_transformpage.h" 38 #include "third_party/pdfium/fpdfsdk/include/fpdfedit.h" 39 #include "third_party/pdfium/fpdfsdk/include/fpdfoom.h" 40 #include "third_party/pdfium/fpdfsdk/include/fpdfppo.h" 41 #include "third_party/pdfium/fpdfsdk/include/fpdfsave.h" 42 #include "third_party/pdfium/fpdfsdk/include/pdfwindow/PDFWindow.h" 43 #include "third_party/pdfium/fpdfsdk/include/pdfwindow/PWL_FontMap.h" 44 #include "ui/events/keycodes/keyboard_codes.h" 45 46 namespace chrome_pdf { 47 48 #define kPageShadowTop 3 49 #define kPageShadowBottom 7 50 #define kPageShadowLeft 5 51 #define kPageShadowRight 5 52 53 #define kPageSeparatorThickness 4 54 #define kHighlightColorR 153 55 #define kHighlightColorG 193 56 #define kHighlightColorB 218 57 58 #define kPendingPageColorR 238 59 #define kPendingPageColorG 238 60 #define kPendingPageColorB 238 61 #define kPendingPageColorA 255 62 63 #define kFormHighlightColor 0xFFE4DD 64 #define kFormHighlightAlpha 100 65 66 #define kMaxPasswordTries 3 67 68 // See Table 3.20 in 69 // http://www.adobe.com/devnet/acrobat/pdfs/pdf_reference_1-7.pdf 70 #define kPDFPermissionPrintLowQualityMask 1 << 2 71 #define kPDFPermissionPrintHighQualityMask 1 << 11 72 #define kPDFPermissionCopyMask 1 << 4 73 #define kPDFPermissionCopyAccessibleMask 1 << 9 74 75 #define kLoadingTextVerticalOffset 50 76 77 // The maximum amount of time we'll spend doing a paint before we give back 78 // control of the thread. 79 #define kMaxProgressivePaintTimeMs 50 80 81 // The maximum amount of time we'll spend doing the first paint. This is less 82 // than the above to keep things smooth if the user is scrolling quickly. We 83 // try painting a little because with accelerated compositing, we get flushes 84 // only every 16 ms. If we were to wait until the next flush to paint the rest 85 // of the pdf, we would never get to draw the pdf and would only draw the 86 // scrollbars. This value is picked to give enough time for gpu related code to 87 // do its thing and still fit within the timelimit for 60Hz. For the 88 // non-composited case, this doesn't make things worse since we're still 89 // painting the scrollbars > 60 Hz. 90 #define kMaxInitialProgressivePaintTimeMs 10 91 92 // Copied from printing/units.cc because we don't want to depend on printing 93 // since it brings in libpng which causes duplicate symbols with PDFium. 94 const int kPointsPerInch = 72; 95 const int kPixelsPerInch = 96; 96 97 struct ClipBox { 98 float left; 99 float right; 100 float top; 101 float bottom; 102 }; 103 104 int ConvertUnit(int value, int old_unit, int new_unit) { 105 // With integer arithmetic, to divide a value with correct rounding, you need 106 // to add half of the divisor value to the dividend value. You need to do the 107 // reverse with negative number. 108 if (value >= 0) { 109 return ((value * new_unit) + (old_unit / 2)) / old_unit; 110 } else { 111 return ((value * new_unit) - (old_unit / 2)) / old_unit; 112 } 113 } 114 115 std::vector<uint32_t> GetPageNumbersFromPrintPageNumberRange( 116 const PP_PrintPageNumberRange_Dev* page_ranges, 117 uint32_t page_range_count) { 118 std::vector<uint32_t> page_numbers; 119 for (uint32_t index = 0; index < page_range_count; ++index) { 120 for (uint32_t page_number = page_ranges[index].first_page_number; 121 page_number <= page_ranges[index].last_page_number; ++page_number) { 122 page_numbers.push_back(page_number); 123 } 124 } 125 return page_numbers; 126 } 127 128 #if defined(OS_LINUX) 129 130 PP_Instance g_last_instance_id; 131 132 struct PDFFontSubstitution { 133 const char* pdf_name; 134 const char* face; 135 bool bold; 136 bool italic; 137 }; 138 139 PP_BrowserFont_Trusted_Weight WeightToBrowserFontTrustedWeight(int weight) { 140 COMPILE_ASSERT(PP_BROWSERFONT_TRUSTED_WEIGHT_100 == 0, 141 PP_BrowserFont_Trusted_Weight_Min); 142 COMPILE_ASSERT(PP_BROWSERFONT_TRUSTED_WEIGHT_900 == 8, 143 PP_BrowserFont_Trusted_Weight_Max); 144 const int kMinimumWeight = 100; 145 const int kMaximumWeight = 900; 146 int normalized_weight = 147 std::min(std::max(weight, kMinimumWeight), kMaximumWeight); 148 normalized_weight = (normalized_weight / 100) - 1; 149 return static_cast<PP_BrowserFont_Trusted_Weight>(normalized_weight); 150 } 151 152 // This list is for CPWL_FontMap::GetDefaultFontByCharset(). 153 // We pretend to have these font natively and let the browser (or underlying 154 // fontconfig) to pick the proper font on the system. 155 void EnumFonts(struct _FPDF_SYSFONTINFO* sysfontinfo, void* mapper) { 156 FPDF_AddInstalledFont(mapper, "Arial", FXFONT_DEFAULT_CHARSET); 157 158 int i = 0; 159 while (CPWL_FontMap::defaultTTFMap[i].charset != -1) { 160 FPDF_AddInstalledFont(mapper, 161 CPWL_FontMap::defaultTTFMap[i].fontname, 162 CPWL_FontMap::defaultTTFMap[i].charset); 163 ++i; 164 } 165 } 166 167 const PDFFontSubstitution PDFFontSubstitutions[] = { 168 {"Courier", "Courier New", false, false}, 169 {"Courier-Bold", "Courier New", true, false}, 170 {"Courier-BoldOblique", "Courier New", true, true}, 171 {"Courier-Oblique", "Courier New", false, true}, 172 {"Helvetica", "Arial", false, false}, 173 {"Helvetica-Bold", "Arial", true, false}, 174 {"Helvetica-BoldOblique", "Arial", true, true}, 175 {"Helvetica-Oblique", "Arial", false, true}, 176 {"Times-Roman", "Times New Roman", false, false}, 177 {"Times-Bold", "Times New Roman", true, false}, 178 {"Times-BoldItalic", "Times New Roman", true, true}, 179 {"Times-Italic", "Times New Roman", false, true}, 180 181 // MS P?(Mincho|Gothic) are the most notable fonts in Japanese PDF files 182 // without embedding the glyphs. Sometimes the font names are encoded 183 // in Japanese Windows's locale (CP932/Shift_JIS) without space. 184 // Most Linux systems don't have the exact font, but for outsourcing 185 // fontconfig to find substitutable font in the system, we pass ASCII 186 // font names to it. 187 {"MS-PGothic", "MS PGothic", false, false}, 188 {"MS-Gothic", "MS Gothic", false, false}, 189 {"MS-PMincho", "MS PMincho", false, false}, 190 {"MS-Mincho", "MS Mincho", false, false}, 191 // MS PGothic in Shift_JIS encoding. 192 {"\x82\x6C\x82\x72\x82\x6F\x83\x53\x83\x56\x83\x62\x83\x4E", 193 "MS PGothic", false, false}, 194 // MS Gothic in Shift_JIS encoding. 195 {"\x82\x6C\x82\x72\x83\x53\x83\x56\x83\x62\x83\x4E", 196 "MS Gothic", false, false}, 197 // MS PMincho in Shift_JIS encoding. 198 {"\x82\x6C\x82\x72\x82\x6F\x96\xBE\x92\xA9", 199 "MS PMincho", false, false}, 200 // MS Mincho in Shift_JIS encoding. 201 {"\x82\x6C\x82\x72\x96\xBE\x92\xA9", 202 "MS Mincho", false, false}, 203 }; 204 205 void* MapFont(struct _FPDF_SYSFONTINFO*, int weight, int italic, 206 int charset, int pitch_family, const char* face, int* exact) { 207 // Do not attempt to map fonts if pepper is not initialized (for privet local 208 // printing). 209 // TODO(noamsml): Real font substitution (http://crbug.com/391978) 210 if (!pp::Module::Get()) 211 return NULL; 212 213 pp::BrowserFontDescription description; 214 215 // Pretend the system does not have the Symbol font to force a fallback to 216 // the built in Symbol font in CFX_FontMapper::FindSubstFont(). 217 if (strcmp(face, "Symbol") == 0) 218 return NULL; 219 220 if (pitch_family & FXFONT_FF_FIXEDPITCH) { 221 description.set_family(PP_BROWSERFONT_TRUSTED_FAMILY_MONOSPACE); 222 } else if (pitch_family & FXFONT_FF_ROMAN) { 223 description.set_family(PP_BROWSERFONT_TRUSTED_FAMILY_SERIF); 224 } 225 226 // Map from the standard PDF fonts to TrueType font names. 227 size_t i; 228 for (i = 0; i < arraysize(PDFFontSubstitutions); ++i) { 229 if (strcmp(face, PDFFontSubstitutions[i].pdf_name) == 0) { 230 description.set_face(PDFFontSubstitutions[i].face); 231 if (PDFFontSubstitutions[i].bold) 232 description.set_weight(PP_BROWSERFONT_TRUSTED_WEIGHT_BOLD); 233 if (PDFFontSubstitutions[i].italic) 234 description.set_italic(true); 235 break; 236 } 237 } 238 239 if (i == arraysize(PDFFontSubstitutions)) { 240 // TODO(kochi): Pass the face in UTF-8. If face is not encoded in UTF-8, 241 // convert to UTF-8 before passing. 242 description.set_face(face); 243 244 description.set_weight(WeightToBrowserFontTrustedWeight(weight)); 245 description.set_italic(italic > 0); 246 } 247 248 if (!pp::PDF::IsAvailable()) { 249 NOTREACHED(); 250 return NULL; 251 } 252 253 PP_Resource font_resource = pp::PDF::GetFontFileWithFallback( 254 pp::InstanceHandle(g_last_instance_id), 255 &description.pp_font_description(), 256 static_cast<PP_PrivateFontCharset>(charset)); 257 long res_id = font_resource; 258 return reinterpret_cast<void*>(res_id); 259 } 260 261 unsigned long GetFontData(struct _FPDF_SYSFONTINFO*, void* font_id, 262 unsigned int table, unsigned char* buffer, 263 unsigned long buf_size) { 264 if (!pp::PDF::IsAvailable()) { 265 NOTREACHED(); 266 return 0; 267 } 268 269 uint32_t size = buf_size; 270 long res_id = reinterpret_cast<long>(font_id); 271 if (!pp::PDF::GetFontTableForPrivateFontFile(res_id, table, buffer, &size)) 272 return 0; 273 return size; 274 } 275 276 void DeleteFont(struct _FPDF_SYSFONTINFO*, void* font_id) { 277 long res_id = reinterpret_cast<long>(font_id); 278 pp::Module::Get()->core()->ReleaseResource(res_id); 279 } 280 281 FPDF_SYSFONTINFO g_font_info = { 282 1, 283 0, 284 EnumFonts, 285 MapFont, 286 0, 287 GetFontData, 288 0, 289 0, 290 DeleteFont 291 }; 292 #endif // defined(OS_LINUX) 293 294 void OOM_Handler(_OOM_INFO*) { 295 // Kill the process. This is important for security, since the code doesn't 296 // NULL-check many memory allocations. If a malloc fails, returns NULL, and 297 // the buffer is then used, it provides a handy mapping of memory starting at 298 // address 0 for an attacker to utilize. 299 abort(); 300 } 301 302 OOM_INFO g_oom_info = { 303 1, 304 OOM_Handler 305 }; 306 307 PDFiumEngine* g_engine_for_unsupported; 308 309 void Unsupported_Handler(UNSUPPORT_INFO*, int type) { 310 if (!g_engine_for_unsupported) { 311 NOTREACHED(); 312 return; 313 } 314 315 g_engine_for_unsupported->UnsupportedFeature(type); 316 } 317 318 UNSUPPORT_INFO g_unsuppored_info = { 319 1, 320 Unsupported_Handler 321 }; 322 323 // Set the destination page size and content area in points based on source 324 // page rotation and orientation. 325 // 326 // |rotated| True if source page is rotated 90 degree or 270 degree. 327 // |is_src_page_landscape| is true if the source page orientation is landscape. 328 // |page_size| has the actual destination page size in points. 329 // |content_rect| has the actual destination page printable area values in 330 // points. 331 void SetPageSizeAndContentRect(bool rotated, 332 bool is_src_page_landscape, 333 pp::Size* page_size, 334 pp::Rect* content_rect) { 335 bool is_dst_page_landscape = page_size->width() > page_size->height(); 336 bool page_orientation_mismatched = is_src_page_landscape != 337 is_dst_page_landscape; 338 bool rotate_dst_page = rotated ^ page_orientation_mismatched; 339 if (rotate_dst_page) { 340 page_size->SetSize(page_size->height(), page_size->width()); 341 content_rect->SetRect(content_rect->y(), content_rect->x(), 342 content_rect->height(), content_rect->width()); 343 } 344 } 345 346 // Calculate the scale factor between |content_rect| and a page of size 347 // |src_width| x |src_height|. 348 // 349 // |scale_to_fit| is true, if we need to calculate the scale factor. 350 // |content_rect| specifies the printable area of the destination page, with 351 // origin at left-bottom. Values are in points. 352 // |src_width| specifies the source page width in points. 353 // |src_height| specifies the source page height in points. 354 // |rotated| True if source page is rotated 90 degree or 270 degree. 355 double CalculateScaleFactor(bool scale_to_fit, 356 const pp::Rect& content_rect, 357 double src_width, double src_height, bool rotated) { 358 if (!scale_to_fit || src_width == 0 || src_height == 0) 359 return 1.0; 360 361 double actual_source_page_width = rotated ? src_height : src_width; 362 double actual_source_page_height = rotated ? src_width : src_height; 363 double ratio_x = static_cast<double>(content_rect.width()) / 364 actual_source_page_width; 365 double ratio_y = static_cast<double>(content_rect.height()) / 366 actual_source_page_height; 367 return std::min(ratio_x, ratio_y); 368 } 369 370 // Compute source clip box boundaries based on the crop box / media box of 371 // source page and scale factor. 372 // 373 // |page| Handle to the source page. Returned by FPDF_LoadPage function. 374 // |scale_factor| specifies the scale factor that should be applied to source 375 // clip box boundaries. 376 // |rotated| True if source page is rotated 90 degree or 270 degree. 377 // |clip_box| out param to hold the computed source clip box values. 378 void CalculateClipBoxBoundary(FPDF_PAGE page, double scale_factor, bool rotated, 379 ClipBox* clip_box) { 380 if (!FPDFPage_GetCropBox(page, &clip_box->left, &clip_box->bottom, 381 &clip_box->right, &clip_box->top)) { 382 if (!FPDFPage_GetMediaBox(page, &clip_box->left, &clip_box->bottom, 383 &clip_box->right, &clip_box->top)) { 384 // Make the default size to be letter size (8.5" X 11"). We are just 385 // following the PDFium way of handling these corner cases. PDFium always 386 // consider US-Letter as the default page size. 387 float paper_width = 612; 388 float paper_height = 792; 389 clip_box->left = 0; 390 clip_box->bottom = 0; 391 clip_box->right = rotated ? paper_height : paper_width; 392 clip_box->top = rotated ? paper_width : paper_height; 393 } 394 } 395 clip_box->left *= scale_factor; 396 clip_box->right *= scale_factor; 397 clip_box->bottom *= scale_factor; 398 clip_box->top *= scale_factor; 399 } 400 401 // Calculate the clip box translation offset for a page that does need to be 402 // scaled. All parameters are in points. 403 // 404 // |content_rect| specifies the printable area of the destination page, with 405 // origin at left-bottom. 406 // |source_clip_box| specifies the source clip box positions, relative to 407 // origin at left-bottom. 408 // |offset_x| and |offset_y| will contain the final translation offsets for the 409 // source clip box, relative to origin at left-bottom. 410 void CalculateScaledClipBoxOffset(const pp::Rect& content_rect, 411 const ClipBox& source_clip_box, 412 double* offset_x, double* offset_y) { 413 const float clip_box_width = source_clip_box.right - source_clip_box.left; 414 const float clip_box_height = source_clip_box.top - source_clip_box.bottom; 415 416 // Center the intended clip region to real clip region. 417 *offset_x = (content_rect.width() - clip_box_width) / 2 + content_rect.x() - 418 source_clip_box.left; 419 *offset_y = (content_rect.height() - clip_box_height) / 2 + content_rect.y() - 420 source_clip_box.bottom; 421 } 422 423 // Calculate the clip box offset for a page that does not need to be scaled. 424 // All parameters are in points. 425 // 426 // |content_rect| specifies the printable area of the destination page, with 427 // origin at left-bottom. 428 // |rotation| specifies the source page rotation values which are N / 90 429 // degrees. 430 // |page_width| specifies the screen destination page width. 431 // |page_height| specifies the screen destination page height. 432 // |source_clip_box| specifies the source clip box positions, relative to origin 433 // at left-bottom. 434 // |offset_x| and |offset_y| will contain the final translation offsets for the 435 // source clip box, relative to origin at left-bottom. 436 void CalculateNonScaledClipBoxOffset(const pp::Rect& content_rect, int rotation, 437 int page_width, int page_height, 438 const ClipBox& source_clip_box, 439 double* offset_x, double* offset_y) { 440 // Align the intended clip region to left-top corner of real clip region. 441 switch (rotation) { 442 case 0: 443 *offset_x = -1 * source_clip_box.left; 444 *offset_y = page_height - source_clip_box.top; 445 break; 446 case 1: 447 *offset_x = 0; 448 *offset_y = -1 * source_clip_box.bottom; 449 break; 450 case 2: 451 *offset_x = page_width - source_clip_box.right; 452 *offset_y = 0; 453 break; 454 case 3: 455 *offset_x = page_height - source_clip_box.right; 456 *offset_y = page_width - source_clip_box.top; 457 break; 458 default: 459 NOTREACHED(); 460 break; 461 } 462 } 463 464 // Do an in-place transformation of objects on |page|. Translate all objects on 465 // |page| in |source_clip_box| by (|offset_x|, |offset_y|) and scale them by 466 // |scale_factor|. 467 // 468 // |page| Handle to the page. Returned by FPDF_LoadPage function. 469 // |source_clip_box| specifies the source clip box positions, relative to 470 // origin at left-bottom. 471 // |scale_factor| specifies the scale factor that should be applied to page 472 // objects. 473 // |offset_x| and |offset_y| specifies the translation offsets for the page 474 // objects, relative to origin at left-bottom. 475 476 void TransformPageObjects(FPDF_PAGE page, const ClipBox& source_clip_box, 477 const double scale_factor, double offset_x, 478 double offset_y) { 479 const int obj_count = FPDFPage_CountObject(page); 480 481 // Create a new clip path. 482 FPDF_CLIPPATH clip_path = FPDF_CreateClipPath( 483 source_clip_box.left + offset_x, source_clip_box.bottom + offset_y, 484 source_clip_box.right + offset_x, source_clip_box.top + offset_y); 485 486 for (int obj_idx = 0; obj_idx < obj_count; ++obj_idx) { 487 FPDF_PAGEOBJECT page_obj = FPDFPage_GetObject(page, obj_idx); 488 FPDFPageObj_Transform(page_obj, scale_factor, 0, 0, scale_factor, 489 offset_x, offset_y); 490 FPDFPageObj_TransformClipPath(page_obj, scale_factor, 0, 0, scale_factor, 491 offset_x, offset_y); 492 } 493 FPDFPage_TransformAnnots(page, scale_factor, 0, 0, scale_factor, 494 offset_x, offset_y); 495 FPDFPage_GenerateContent(page); 496 497 // Add a extra clip path to the new pdf page here. 498 FPDFPage_InsertClipPath(page, clip_path); 499 500 // Destroy the clip path. 501 FPDF_DestroyClipPath(clip_path); 502 } 503 504 bool InitializeSDK(void* data) { 505 FPDF_InitLibrary(data); 506 507 #if defined(OS_LINUX) 508 // Font loading doesn't work in the renderer sandbox in Linux. 509 FPDF_SetSystemFontInfo(&g_font_info); 510 #endif 511 512 FSDK_SetOOMHandler(&g_oom_info); 513 FSDK_SetUnSpObjProcessHandler(&g_unsuppored_info); 514 515 return true; 516 } 517 518 void ShutdownSDK() { 519 FPDF_DestroyLibrary(); 520 } 521 522 PDFEngine* PDFEngine::Create(PDFEngine::Client* client) { 523 return new PDFiumEngine(client); 524 } 525 526 PDFiumEngine::PDFiumEngine(PDFEngine::Client* client) 527 : client_(client), 528 current_zoom_(1.0), 529 current_rotation_(0), 530 doc_loader_(this), 531 password_tries_remaining_(0), 532 doc_(NULL), 533 form_(NULL), 534 defer_page_unload_(false), 535 selecting_(false), 536 next_page_to_search_(-1), 537 last_page_to_search_(-1), 538 last_character_index_to_search_(-1), 539 current_find_index_(-1), 540 permissions_(0), 541 fpdf_availability_(NULL), 542 next_timer_id_(0), 543 last_page_mouse_down_(-1), 544 first_visible_page_(-1), 545 most_visible_page_(-1), 546 called_do_document_action_(false), 547 render_grayscale_(false), 548 progressive_paint_timeout_(0), 549 getting_password_(false) { 550 find_factory_.Initialize(this); 551 password_factory_.Initialize(this); 552 553 file_access_.m_FileLen = 0; 554 file_access_.m_GetBlock = &GetBlock; 555 file_access_.m_Param = &doc_loader_; 556 557 file_availability_.version = 1; 558 file_availability_.IsDataAvail = &IsDataAvail; 559 file_availability_.loader = &doc_loader_; 560 561 download_hints_.version = 1; 562 download_hints_.AddSegment = &AddSegment; 563 download_hints_.loader = &doc_loader_; 564 565 // Initialize FPDF_FORMFILLINFO member variables. Deriving from this struct 566 // allows the static callbacks to be able to cast the FPDF_FORMFILLINFO in 567 // callbacks to ourself instead of maintaining a map of them to 568 // PDFiumEngine. 569 FPDF_FORMFILLINFO::version = 1; 570 FPDF_FORMFILLINFO::m_pJsPlatform = this; 571 FPDF_FORMFILLINFO::Release = NULL; 572 FPDF_FORMFILLINFO::FFI_Invalidate = Form_Invalidate; 573 FPDF_FORMFILLINFO::FFI_OutputSelectedRect = Form_OutputSelectedRect; 574 FPDF_FORMFILLINFO::FFI_SetCursor = Form_SetCursor; 575 FPDF_FORMFILLINFO::FFI_SetTimer = Form_SetTimer; 576 FPDF_FORMFILLINFO::FFI_KillTimer = Form_KillTimer; 577 FPDF_FORMFILLINFO::FFI_GetLocalTime = Form_GetLocalTime; 578 FPDF_FORMFILLINFO::FFI_OnChange = Form_OnChange; 579 FPDF_FORMFILLINFO::FFI_GetPage = Form_GetPage; 580 FPDF_FORMFILLINFO::FFI_GetCurrentPage = Form_GetCurrentPage; 581 FPDF_FORMFILLINFO::FFI_GetRotation = Form_GetRotation; 582 FPDF_FORMFILLINFO::FFI_ExecuteNamedAction = Form_ExecuteNamedAction; 583 FPDF_FORMFILLINFO::FFI_SetTextFieldFocus = Form_SetTextFieldFocus; 584 FPDF_FORMFILLINFO::FFI_DoURIAction = Form_DoURIAction; 585 FPDF_FORMFILLINFO::FFI_DoGoToAction = Form_DoGoToAction; 586 587 IPDF_JSPLATFORM::version = 1; 588 IPDF_JSPLATFORM::app_alert = Form_Alert; 589 IPDF_JSPLATFORM::app_beep = Form_Beep; 590 IPDF_JSPLATFORM::app_response = Form_Response; 591 IPDF_JSPLATFORM::Doc_getFilePath = Form_GetFilePath; 592 IPDF_JSPLATFORM::Doc_mail = Form_Mail; 593 IPDF_JSPLATFORM::Doc_print = Form_Print; 594 IPDF_JSPLATFORM::Doc_submitForm = Form_SubmitForm; 595 IPDF_JSPLATFORM::Doc_gotoPage = Form_GotoPage; 596 IPDF_JSPLATFORM::Field_browse = Form_Browse; 597 598 IFSDK_PAUSE::version = 1; 599 IFSDK_PAUSE::user = NULL; 600 IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow; 601 } 602 603 PDFiumEngine::~PDFiumEngine() { 604 STLDeleteElements(&pages_); 605 if (doc_) { 606 if (form_) { 607 FORM_DoDocumentAAction(form_, FPDFDOC_AACTION_WC); 608 FPDFDOC_ExitFormFillEnviroument(form_); 609 } 610 FPDF_CloseDocument(doc_); 611 } 612 613 if (fpdf_availability_) 614 FPDFAvail_Destroy(fpdf_availability_); 615 } 616 617 int PDFiumEngine::GetBlock(void* param, unsigned long position, 618 unsigned char* buffer, unsigned long size) { 619 DocumentLoader* loader = static_cast<DocumentLoader*>(param); 620 return loader->GetBlock(position, size, buffer); 621 } 622 623 bool PDFiumEngine::IsDataAvail(FX_FILEAVAIL* param, 624 size_t offset, size_t size) { 625 PDFiumEngine::FileAvail* file_avail = 626 static_cast<PDFiumEngine::FileAvail*>(param); 627 return file_avail->loader->IsDataAvailable(offset, size); 628 } 629 630 void PDFiumEngine::AddSegment(FX_DOWNLOADHINTS* param, 631 size_t offset, size_t size) { 632 PDFiumEngine::DownloadHints* download_hints = 633 static_cast<PDFiumEngine::DownloadHints*>(param); 634 return download_hints->loader->RequestData(offset, size); 635 } 636 637 bool PDFiumEngine::New(const char* url) { 638 url_ = url; 639 headers_ = std::string(); 640 return true; 641 } 642 643 bool PDFiumEngine::New(const char* url, 644 const char* headers) { 645 url_ = url; 646 if (!headers) 647 headers_ = std::string(); 648 else 649 headers_ = headers; 650 return true; 651 } 652 653 void PDFiumEngine::PageOffsetUpdated(const pp::Point& page_offset) { 654 page_offset_ = page_offset; 655 } 656 657 void PDFiumEngine::PluginSizeUpdated(const pp::Size& size) { 658 CancelPaints(); 659 660 plugin_size_ = size; 661 CalculateVisiblePages(); 662 } 663 664 void PDFiumEngine::ScrolledToXPosition(int position) { 665 CancelPaints(); 666 667 int old_x = position_.x(); 668 position_.set_x(position); 669 CalculateVisiblePages(); 670 client_->Scroll(pp::Point(old_x - position, 0)); 671 } 672 673 void PDFiumEngine::ScrolledToYPosition(int position) { 674 CancelPaints(); 675 676 int old_y = position_.y(); 677 position_.set_y(position); 678 CalculateVisiblePages(); 679 client_->Scroll(pp::Point(0, old_y - position)); 680 } 681 682 void PDFiumEngine::PrePaint() { 683 for (size_t i = 0; i < progressive_paints_.size(); ++i) 684 progressive_paints_[i].painted_ = false; 685 } 686 687 void PDFiumEngine::Paint(const pp::Rect& rect, 688 pp::ImageData* image_data, 689 std::vector<pp::Rect>* ready, 690 std::vector<pp::Rect>* pending) { 691 pp::Rect leftover = rect; 692 for (size_t i = 0; i < visible_pages_.size(); ++i) { 693 int index = visible_pages_[i]; 694 pp::Rect page_rect = pages_[index]->rect(); 695 // Convert the current page's rectangle to screen rectangle. We do this 696 // instead of the reverse (converting the dirty rectangle from screen to 697 // page coordinates) because then we'd have to convert back to screen 698 // coordinates, and the rounding errors sometime leave pixels dirty or even 699 // move the text up or down a pixel when zoomed. 700 pp::Rect page_rect_in_screen = GetPageScreenRect(index); 701 pp::Rect dirty_in_screen = page_rect_in_screen.Intersect(leftover); 702 if (dirty_in_screen.IsEmpty()) 703 continue; 704 705 leftover = leftover.Subtract(dirty_in_screen); 706 707 if (pages_[index]->available()) { 708 int progressive = GetProgressiveIndex(index); 709 if (progressive != -1 && 710 progressive_paints_[progressive].rect != dirty_in_screen) { 711 // The PDFium code can only handle one progressive paint at a time, so 712 // queue this up. Previously we used to merge the rects when this 713 // happened, but it made scrolling up on complex PDFs very slow since 714 // there would be a damaged rect at the top (from scroll) and at the 715 // bottom (from toolbar). 716 pending->push_back(dirty_in_screen); 717 continue; 718 } 719 720 if (progressive == -1) { 721 progressive = StartPaint(index, dirty_in_screen); 722 progressive_paint_timeout_ = kMaxInitialProgressivePaintTimeMs; 723 } else { 724 progressive_paint_timeout_ = kMaxProgressivePaintTimeMs; 725 } 726 727 progressive_paints_[progressive].painted_ = true; 728 if (ContinuePaint(progressive, image_data)) { 729 FinishPaint(progressive, image_data); 730 ready->push_back(dirty_in_screen); 731 } else { 732 pending->push_back(dirty_in_screen); 733 } 734 } else { 735 PaintUnavailablePage(index, dirty_in_screen, image_data); 736 ready->push_back(dirty_in_screen); 737 } 738 } 739 } 740 741 void PDFiumEngine::PostPaint() { 742 for (size_t i = 0; i < progressive_paints_.size(); ++i) { 743 if (progressive_paints_[i].painted_) 744 continue; 745 746 // This rectangle must have been merged with another one, that's why we 747 // weren't asked to paint it. Remove it or otherwise we'll never finish 748 // painting. 749 FPDF_RenderPage_Close( 750 pages_[progressive_paints_[i].page_index]->GetPage()); 751 FPDFBitmap_Destroy(progressive_paints_[i].bitmap); 752 progressive_paints_.erase(progressive_paints_.begin() + i); 753 --i; 754 } 755 } 756 757 bool PDFiumEngine::HandleDocumentLoad(const pp::URLLoader& loader) { 758 password_tries_remaining_ = kMaxPasswordTries; 759 return doc_loader_.Init(loader, url_, headers_); 760 } 761 762 pp::Instance* PDFiumEngine::GetPluginInstance() { 763 return client_->GetPluginInstance(); 764 } 765 766 pp::URLLoader PDFiumEngine::CreateURLLoader() { 767 return client_->CreateURLLoader(); 768 } 769 770 void PDFiumEngine::AppendPage(PDFEngine* engine, int index) { 771 // Unload and delete the blank page before appending. 772 pages_[index]->Unload(); 773 pages_[index]->set_calculated_links(false); 774 pp::Size curr_page_size = GetPageSize(index); 775 FPDFPage_Delete(doc_, index); 776 FPDF_ImportPages(doc_, 777 static_cast<PDFiumEngine*>(engine)->doc(), 778 "1", 779 index); 780 pp::Size new_page_size = GetPageSize(index); 781 if (curr_page_size != new_page_size) 782 LoadPageInfo(true); 783 client_->Invalidate(GetPageScreenRect(index)); 784 } 785 786 pp::Point PDFiumEngine::GetScrollPosition() { 787 return position_; 788 } 789 790 void PDFiumEngine::SetScrollPosition(const pp::Point& position) { 791 position_ = position; 792 } 793 794 bool PDFiumEngine::IsProgressiveLoad() { 795 return doc_loader_.is_partial_document(); 796 } 797 798 void PDFiumEngine::OnPartialDocumentLoaded() { 799 file_access_.m_FileLen = doc_loader_.document_size(); 800 fpdf_availability_ = FPDFAvail_Create(&file_availability_, &file_access_); 801 DCHECK(fpdf_availability_); 802 803 // Currently engine does not deal efficiently with some non-linearized files. 804 // See http://code.google.com/p/chromium/issues/detail?id=59400 805 // To improve user experience we download entire file for non-linearized PDF. 806 if (!FPDFAvail_IsLinearized(fpdf_availability_)) { 807 doc_loader_.RequestData(0, doc_loader_.document_size()); 808 return; 809 } 810 811 LoadDocument(); 812 } 813 814 void PDFiumEngine::OnPendingRequestComplete() { 815 if (!doc_ || !form_) { 816 LoadDocument(); 817 return; 818 } 819 820 // LoadDocument() will result in |pending_pages_| being reset so there's no 821 // need to run the code below in that case. 822 bool update_pages = false; 823 std::vector<int> still_pending; 824 for (size_t i = 0; i < pending_pages_.size(); ++i) { 825 if (CheckPageAvailable(pending_pages_[i], &still_pending)) { 826 update_pages = true; 827 if (IsPageVisible(pending_pages_[i])) 828 client_->Invalidate(GetPageScreenRect(pending_pages_[i])); 829 } 830 } 831 pending_pages_.swap(still_pending); 832 if (update_pages) 833 LoadPageInfo(true); 834 } 835 836 void PDFiumEngine::OnNewDataAvailable() { 837 client_->DocumentLoadProgress(doc_loader_.GetAvailableData(), 838 doc_loader_.document_size()); 839 } 840 841 void PDFiumEngine::OnDocumentComplete() { 842 if (!doc_ || !form_) { 843 file_access_.m_FileLen = doc_loader_.document_size(); 844 LoadDocument(); 845 return; 846 } 847 848 bool need_update = false; 849 for (size_t i = 0; i < pages_.size(); ++i) { 850 if (pages_[i]->available()) 851 continue; 852 853 pages_[i]->set_available(true); 854 // We still need to call IsPageAvail() even if the whole document is 855 // already downloaded. 856 FPDFAvail_IsPageAvail(fpdf_availability_, i, &download_hints_); 857 need_update = true; 858 if (IsPageVisible(i)) 859 client_->Invalidate(GetPageScreenRect(i)); 860 } 861 if (need_update) 862 LoadPageInfo(true); 863 864 FinishLoadingDocument(); 865 } 866 867 void PDFiumEngine::FinishLoadingDocument() { 868 DCHECK(doc_loader_.IsDocumentComplete() && doc_); 869 if (called_do_document_action_) 870 return; 871 called_do_document_action_ = true; 872 873 // These can only be called now, as the JS might end up needing a page. 874 FORM_DoDocumentJSAction(form_); 875 FORM_DoDocumentOpenAction(form_); 876 if (most_visible_page_ != -1) { 877 FPDF_PAGE new_page = pages_[most_visible_page_]->GetPage(); 878 FORM_DoPageAAction(new_page, form_, FPDFPAGE_AACTION_OPEN); 879 } 880 881 if (doc_) // This can only happen if loading |doc_| fails. 882 client_->DocumentLoadComplete(pages_.size()); 883 } 884 885 void PDFiumEngine::UnsupportedFeature(int type) { 886 std::string feature; 887 switch (type) { 888 case FPDF_UNSP_DOC_XFAFORM: 889 feature = "XFA"; 890 break; 891 case FPDF_UNSP_DOC_PORTABLECOLLECTION: 892 feature = "Portfolios_Packages"; 893 break; 894 case FPDF_UNSP_DOC_ATTACHMENT: 895 case FPDF_UNSP_ANNOT_ATTACHMENT: 896 feature = "Attachment"; 897 break; 898 case FPDF_UNSP_DOC_SECURITY: 899 feature = "Rights_Management"; 900 break; 901 case FPDF_UNSP_DOC_SHAREDREVIEW: 902 feature = "Shared_Review"; 903 break; 904 case FPDF_UNSP_DOC_SHAREDFORM_ACROBAT: 905 case FPDF_UNSP_DOC_SHAREDFORM_FILESYSTEM: 906 case FPDF_UNSP_DOC_SHAREDFORM_EMAIL: 907 feature = "Shared_Form"; 908 break; 909 case FPDF_UNSP_ANNOT_3DANNOT: 910 feature = "3D"; 911 break; 912 case FPDF_UNSP_ANNOT_MOVIE: 913 feature = "Movie"; 914 break; 915 case FPDF_UNSP_ANNOT_SOUND: 916 feature = "Sound"; 917 break; 918 case FPDF_UNSP_ANNOT_SCREEN_MEDIA: 919 case FPDF_UNSP_ANNOT_SCREEN_RICHMEDIA: 920 feature = "Screen"; 921 break; 922 case FPDF_UNSP_ANNOT_SIG: 923 feature = "Digital_Signature"; 924 break; 925 } 926 client_->DocumentHasUnsupportedFeature(feature); 927 } 928 929 void PDFiumEngine::ContinueFind(int32_t result) { 930 StartFind(current_find_text_.c_str(), !!result); 931 } 932 933 bool PDFiumEngine::HandleEvent(const pp::InputEvent& event) { 934 DCHECK(!defer_page_unload_); 935 defer_page_unload_ = true; 936 bool rv = false; 937 switch (event.GetType()) { 938 case PP_INPUTEVENT_TYPE_MOUSEDOWN: 939 rv = OnMouseDown(pp::MouseInputEvent(event)); 940 break; 941 case PP_INPUTEVENT_TYPE_MOUSEUP: 942 rv = OnMouseUp(pp::MouseInputEvent(event)); 943 break; 944 case PP_INPUTEVENT_TYPE_MOUSEMOVE: 945 rv = OnMouseMove(pp::MouseInputEvent(event)); 946 break; 947 case PP_INPUTEVENT_TYPE_KEYDOWN: 948 rv = OnKeyDown(pp::KeyboardInputEvent(event)); 949 break; 950 case PP_INPUTEVENT_TYPE_KEYUP: 951 rv = OnKeyUp(pp::KeyboardInputEvent(event)); 952 break; 953 case PP_INPUTEVENT_TYPE_CHAR: 954 rv = OnChar(pp::KeyboardInputEvent(event)); 955 break; 956 default: 957 break; 958 } 959 960 DCHECK(defer_page_unload_); 961 defer_page_unload_ = false; 962 for (size_t i = 0; i < deferred_page_unloads_.size(); ++i) 963 pages_[deferred_page_unloads_[i]]->Unload(); 964 deferred_page_unloads_.clear(); 965 return rv; 966 } 967 968 uint32_t PDFiumEngine::QuerySupportedPrintOutputFormats() { 969 if (!HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY)) 970 return 0; 971 return PP_PRINTOUTPUTFORMAT_PDF; 972 } 973 974 void PDFiumEngine::PrintBegin() { 975 FORM_DoDocumentAAction(form_, FPDFDOC_AACTION_WP); 976 } 977 978 pp::Resource PDFiumEngine::PrintPages( 979 const PP_PrintPageNumberRange_Dev* page_ranges, uint32_t page_range_count, 980 const PP_PrintSettings_Dev& print_settings) { 981 if (HasPermission(PDFEngine::PERMISSION_PRINT_HIGH_QUALITY)) 982 return PrintPagesAsPDF(page_ranges, page_range_count, print_settings); 983 else if (HasPermission(PDFEngine::PERMISSION_PRINT_LOW_QUALITY)) 984 return PrintPagesAsRasterPDF(page_ranges, page_range_count, print_settings); 985 return pp::Resource(); 986 } 987 988 pp::Buffer_Dev PDFiumEngine::PrintPagesAsRasterPDF( 989 const PP_PrintPageNumberRange_Dev* page_ranges, uint32_t page_range_count, 990 const PP_PrintSettings_Dev& print_settings) { 991 if (!page_range_count) 992 return pp::Buffer_Dev(); 993 994 // If document is not downloaded yet, disable printing. 995 if (doc_ && !doc_loader_.IsDocumentComplete()) 996 return pp::Buffer_Dev(); 997 998 FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument(); 999 if (!output_doc) 1000 return pp::Buffer_Dev(); 1001 1002 SaveSelectedFormForPrint(); 1003 1004 std::vector<PDFiumPage> pages_to_print; 1005 // width and height of source PDF pages. 1006 std::vector<std::pair<double, double> > source_page_sizes; 1007 // Collect pages to print and sizes of source pages. 1008 std::vector<uint32_t> page_numbers = 1009 GetPageNumbersFromPrintPageNumberRange(page_ranges, page_range_count); 1010 for (size_t i = 0; i < page_numbers.size(); ++i) { 1011 uint32_t page_number = page_numbers[i]; 1012 FPDF_PAGE pdf_page = FPDF_LoadPage(doc_, page_number); 1013 double source_page_width = FPDF_GetPageWidth(pdf_page); 1014 double source_page_height = FPDF_GetPageHeight(pdf_page); 1015 source_page_sizes.push_back(std::make_pair(source_page_width, 1016 source_page_height)); 1017 1018 int width_in_pixels = ConvertUnit(source_page_width, 1019 static_cast<int>(kPointsPerInch), 1020 print_settings.dpi); 1021 int height_in_pixels = ConvertUnit(source_page_height, 1022 static_cast<int>(kPointsPerInch), 1023 print_settings.dpi); 1024 1025 pp::Rect rect(width_in_pixels, height_in_pixels); 1026 pages_to_print.push_back(PDFiumPage(this, page_number, rect, true)); 1027 FPDF_ClosePage(pdf_page); 1028 } 1029 1030 #if defined(OS_LINUX) 1031 g_last_instance_id = client_->GetPluginInstance()->pp_instance(); 1032 #endif 1033 1034 size_t i = 0; 1035 for (; i < pages_to_print.size(); ++i) { 1036 double source_page_width = source_page_sizes[i].first; 1037 double source_page_height = source_page_sizes[i].second; 1038 const pp::Size& bitmap_size(pages_to_print[i].rect().size()); 1039 1040 // Use temp_doc to compress image by saving PDF to buffer. 1041 FPDF_DOCUMENT temp_doc = FPDF_CreateNewDocument(); 1042 if (!temp_doc) 1043 break; 1044 1045 FPDF_PAGE output_page = FPDFPage_New(temp_doc, 0, source_page_width, 1046 source_page_height); 1047 1048 pp::ImageData image = pp::ImageData(client_->GetPluginInstance(), 1049 PP_IMAGEDATAFORMAT_BGRA_PREMUL, 1050 bitmap_size, 1051 false); 1052 1053 FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(bitmap_size.width(), 1054 bitmap_size.height(), 1055 FPDFBitmap_BGRx, 1056 image.data(), 1057 image.stride()); 1058 1059 // Clear the bitmap 1060 FPDFBitmap_FillRect(bitmap, 0, 0, bitmap_size.width(), 1061 bitmap_size.height(), 255, 255, 255, 255); 1062 1063 pp::Rect page_rect = pages_to_print[i].rect(); 1064 FPDF_RenderPageBitmap(bitmap, pages_to_print[i].GetPrintPage(), 1065 page_rect.x(), page_rect.y(), 1066 page_rect.width(), page_rect.height(), 1067 print_settings.orientation, 1068 FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH); 1069 1070 double ratio_x = (static_cast<double>(bitmap_size.width()) * 1071 kPointsPerInch) / print_settings.dpi; 1072 double ratio_y = (static_cast<double>(bitmap_size.height()) * 1073 kPointsPerInch) / print_settings.dpi; 1074 1075 // Add the bitmap to an image object and add the image object to the output 1076 // page. 1077 FPDF_PAGEOBJECT img_obj = FPDFPageObj_NewImgeObj(output_doc); 1078 FPDFImageObj_SetBitmap(&output_page, 1, img_obj, bitmap); 1079 FPDFImageObj_SetMatrix(img_obj, ratio_x, 0, 0, ratio_y, 0, 0); 1080 FPDFPage_InsertObject(output_page, img_obj); 1081 FPDFPage_GenerateContent(output_page); 1082 FPDF_ClosePage(output_page); 1083 1084 pages_to_print[i].ClosePrintPage(); 1085 FPDFBitmap_Destroy(bitmap); 1086 1087 pp::Buffer_Dev buffer = GetFlattenedPrintData(temp_doc); 1088 FPDF_CloseDocument(temp_doc); 1089 1090 PDFiumMemBufferFileRead file_read(buffer.data(), buffer.size()); 1091 temp_doc = FPDF_LoadCustomDocument(&file_read, NULL); 1092 if (!temp_doc) 1093 break; 1094 1095 FPDF_BOOL imported = FPDF_ImportPages(output_doc, temp_doc, "1", i); 1096 FPDF_CloseDocument(temp_doc); 1097 if (!imported) 1098 break; 1099 } 1100 1101 pp::Buffer_Dev buffer; 1102 if (i == pages_to_print.size()) { 1103 FPDF_CopyViewerPreferences(output_doc, doc_); 1104 FitContentsToPrintableAreaIfRequired(output_doc, print_settings); 1105 // Now flatten all the output pages. 1106 buffer = GetFlattenedPrintData(output_doc); 1107 } 1108 FPDF_CloseDocument(output_doc); 1109 return buffer; 1110 } 1111 1112 pp::Buffer_Dev PDFiumEngine::GetFlattenedPrintData(const FPDF_DOCUMENT& doc) { 1113 int page_count = FPDF_GetPageCount(doc); 1114 bool flatten_succeeded = true; 1115 for (int i = 0; i < page_count; ++i) { 1116 FPDF_PAGE page = FPDF_LoadPage(doc, i); 1117 DCHECK(page); 1118 if (page) { 1119 int flatten_ret = FPDFPage_Flatten(page, FLAT_PRINT); 1120 FPDF_ClosePage(page); 1121 if (flatten_ret == FLATTEN_FAIL) { 1122 flatten_succeeded = false; 1123 break; 1124 } 1125 } else { 1126 flatten_succeeded = false; 1127 break; 1128 } 1129 } 1130 if (!flatten_succeeded) { 1131 FPDF_CloseDocument(doc); 1132 return pp::Buffer_Dev(); 1133 } 1134 1135 pp::Buffer_Dev buffer; 1136 PDFiumMemBufferFileWrite output_file_write; 1137 if (FPDF_SaveAsCopy(doc, &output_file_write, 0)) { 1138 buffer = pp::Buffer_Dev( 1139 client_->GetPluginInstance(), output_file_write.size()); 1140 if (!buffer.is_null()) { 1141 memcpy(buffer.data(), output_file_write.buffer().c_str(), 1142 output_file_write.size()); 1143 } 1144 } 1145 return buffer; 1146 } 1147 1148 pp::Buffer_Dev PDFiumEngine::PrintPagesAsPDF( 1149 const PP_PrintPageNumberRange_Dev* page_ranges, uint32_t page_range_count, 1150 const PP_PrintSettings_Dev& print_settings) { 1151 if (!page_range_count) 1152 return pp::Buffer_Dev(); 1153 1154 DCHECK(doc_); 1155 FPDF_DOCUMENT output_doc = FPDF_CreateNewDocument(); 1156 if (!output_doc) 1157 return pp::Buffer_Dev(); 1158 1159 SaveSelectedFormForPrint(); 1160 1161 std::string page_number_str; 1162 for (uint32_t index = 0; index < page_range_count; ++index) { 1163 if (!page_number_str.empty()) 1164 page_number_str.append(","); 1165 page_number_str.append( 1166 base::IntToString(page_ranges[index].first_page_number + 1)); 1167 if (page_ranges[index].first_page_number != 1168 page_ranges[index].last_page_number) { 1169 page_number_str.append("-"); 1170 page_number_str.append( 1171 base::IntToString(page_ranges[index].last_page_number + 1)); 1172 } 1173 } 1174 1175 std::vector<uint32_t> page_numbers = 1176 GetPageNumbersFromPrintPageNumberRange(page_ranges, page_range_count); 1177 for (size_t i = 0; i < page_numbers.size(); ++i) { 1178 uint32_t page_number = page_numbers[i]; 1179 pages_[page_number]->GetPage(); 1180 if (!IsPageVisible(page_numbers[i])) 1181 pages_[page_number]->Unload(); 1182 } 1183 1184 FPDF_CopyViewerPreferences(output_doc, doc_); 1185 if (!FPDF_ImportPages(output_doc, doc_, page_number_str.c_str(), 0)) { 1186 FPDF_CloseDocument(output_doc); 1187 return pp::Buffer_Dev(); 1188 } 1189 1190 FitContentsToPrintableAreaIfRequired(output_doc, print_settings); 1191 1192 // Now flatten all the output pages. 1193 pp::Buffer_Dev buffer = GetFlattenedPrintData(output_doc); 1194 FPDF_CloseDocument(output_doc); 1195 return buffer; 1196 } 1197 1198 void PDFiumEngine::FitContentsToPrintableAreaIfRequired( 1199 const FPDF_DOCUMENT& doc, const PP_PrintSettings_Dev& print_settings) { 1200 // Check to see if we need to fit pdf contents to printer paper size. 1201 if (print_settings.print_scaling_option != 1202 PP_PRINTSCALINGOPTION_SOURCE_SIZE) { 1203 int num_pages = FPDF_GetPageCount(doc); 1204 // In-place transformation is more efficient than creating a new 1205 // transformed document from the source document. Therefore, transform 1206 // every page to fit the contents in the selected printer paper. 1207 for (int i = 0; i < num_pages; ++i) { 1208 FPDF_PAGE page = FPDF_LoadPage(doc, i); 1209 TransformPDFPageForPrinting(page, print_settings); 1210 FPDF_ClosePage(page); 1211 } 1212 } 1213 } 1214 1215 void PDFiumEngine::SaveSelectedFormForPrint() { 1216 FORM_ForceToKillFocus(form_); 1217 client_->FormTextFieldFocusChange(false); 1218 } 1219 1220 void PDFiumEngine::PrintEnd() { 1221 FORM_DoDocumentAAction(form_, FPDFDOC_AACTION_DP); 1222 } 1223 1224 PDFiumPage::Area PDFiumEngine::GetCharIndex( 1225 const pp::MouseInputEvent& event, int* page_index, 1226 int* char_index, PDFiumPage::LinkTarget* target) { 1227 // First figure out which page this is in. 1228 pp::Point mouse_point = event.GetPosition(); 1229 pp::Point point( 1230 static_cast<int>((mouse_point.x() + position_.x()) / current_zoom_), 1231 static_cast<int>((mouse_point.y() + position_.y()) / current_zoom_)); 1232 return GetCharIndex(point, page_index, char_index, target); 1233 } 1234 1235 PDFiumPage::Area PDFiumEngine::GetCharIndex( 1236 const pp::Point& point, 1237 int* page_index, 1238 int* char_index, 1239 PDFiumPage::LinkTarget* target) { 1240 int page = -1; 1241 for (size_t i = 0; i < visible_pages_.size(); ++i) { 1242 if (pages_[visible_pages_[i]]->rect().Contains(point)) { 1243 page = visible_pages_[i]; 1244 break; 1245 } 1246 } 1247 if (page == -1) 1248 return PDFiumPage::NONSELECTABLE_AREA; 1249 1250 // If the page hasn't finished rendering, calling into the page sometimes 1251 // leads to hangs. 1252 for (size_t i = 0; i < progressive_paints_.size(); ++i) { 1253 if (progressive_paints_[i].page_index == page) 1254 return PDFiumPage::NONSELECTABLE_AREA; 1255 } 1256 1257 *page_index = page; 1258 return pages_[page]->GetCharIndex(point, current_rotation_, char_index, 1259 target); 1260 } 1261 1262 bool PDFiumEngine::OnMouseDown(const pp::MouseInputEvent& event) { 1263 if (event.GetButton() != PP_INPUTEVENT_MOUSEBUTTON_LEFT) 1264 return false; 1265 1266 SelectionChangeInvalidator selection_invalidator(this); 1267 selection_.clear(); 1268 1269 int page_index = -1; 1270 int char_index = -1; 1271 PDFiumPage::LinkTarget target; 1272 PDFiumPage::Area area = GetCharIndex(event, &page_index, 1273 &char_index, &target); 1274 if (area == PDFiumPage::WEBLINK_AREA) { 1275 bool open_in_new_tab = !!(event.GetModifiers() & kDefaultKeyModifier); 1276 client_->NavigateTo(target.url, open_in_new_tab); 1277 client_->FormTextFieldFocusChange(false); 1278 return true; 1279 } 1280 1281 if (area == PDFiumPage::DOCLINK_AREA) { 1282 client_->ScrollToPage(target.page); 1283 client_->FormTextFieldFocusChange(false); 1284 return true; 1285 } 1286 1287 if (page_index != -1) { 1288 last_page_mouse_down_ = page_index; 1289 double page_x, page_y; 1290 pp::Point point = event.GetPosition(); 1291 DeviceToPage(page_index, point.x(), point.y(), &page_x, &page_y); 1292 1293 FORM_OnLButtonDown(form_, pages_[page_index]->GetPage(), 0, page_x, page_y); 1294 int control = FPDPage_HasFormFieldAtPoint( 1295 form_, pages_[page_index]->GetPage(), page_x, page_y); 1296 if (control > FPDF_FORMFIELD_UNKNOWN) { // returns -1 sometimes... 1297 client_->FormTextFieldFocusChange(control == FPDF_FORMFIELD_TEXTFIELD || 1298 control == FPDF_FORMFIELD_COMBOBOX); 1299 return true; // Return now before we get into the selection code. 1300 } 1301 } 1302 1303 client_->FormTextFieldFocusChange(false); 1304 1305 if (area != PDFiumPage::TEXT_AREA) 1306 return true; // Return true so WebKit doesn't do its own highlighting. 1307 1308 if (event.GetClickCount() == 1) { 1309 OnSingleClick(page_index, char_index); 1310 } else if (event.GetClickCount() == 2 || 1311 event.GetClickCount() == 3) { 1312 OnMultipleClick(event.GetClickCount(), page_index, char_index); 1313 } 1314 1315 return true; 1316 } 1317 1318 void PDFiumEngine::OnSingleClick(int page_index, int char_index) { 1319 selecting_ = true; 1320 selection_.push_back(PDFiumRange(pages_[page_index], char_index, 0)); 1321 } 1322 1323 void PDFiumEngine::OnMultipleClick(int click_count, 1324 int page_index, 1325 int char_index) { 1326 // It would be more efficient if the SDK could support finding a space, but 1327 // now it doesn't. 1328 int start_index = char_index; 1329 do { 1330 base::char16 cur = pages_[page_index]->GetCharAtIndex(start_index); 1331 // For double click, we want to select one word so we look for whitespace 1332 // boundaries. For triple click, we want the whole line. 1333 if (cur == '\n' || (click_count == 2 && (cur == ' ' || cur == '\t'))) 1334 break; 1335 } while (--start_index >= 0); 1336 if (start_index) 1337 start_index++; 1338 1339 int end_index = char_index; 1340 int total = pages_[page_index]->GetCharCount(); 1341 while (end_index++ <= total) { 1342 base::char16 cur = pages_[page_index]->GetCharAtIndex(end_index); 1343 if (cur == '\n' || (click_count == 2 && (cur == ' ' || cur == '\t'))) 1344 break; 1345 } 1346 1347 selection_.push_back(PDFiumRange( 1348 pages_[page_index], start_index, end_index - start_index)); 1349 } 1350 1351 bool PDFiumEngine::OnMouseUp(const pp::MouseInputEvent& event) { 1352 if (event.GetButton() != PP_INPUTEVENT_MOUSEBUTTON_LEFT) 1353 return false; 1354 1355 int page_index = -1; 1356 int char_index = -1; 1357 GetCharIndex(event, &page_index, &char_index, NULL); 1358 if (page_index != -1) { 1359 double page_x, page_y; 1360 pp::Point point = event.GetPosition(); 1361 DeviceToPage(page_index, point.x(), point.y(), &page_x, &page_y); 1362 FORM_OnLButtonUp( 1363 form_, pages_[page_index]->GetPage(), 0, page_x, page_y); 1364 } 1365 1366 if (!selecting_) 1367 return false; 1368 1369 selecting_ = false; 1370 return true; 1371 } 1372 1373 bool PDFiumEngine::OnMouseMove(const pp::MouseInputEvent& event) { 1374 int page_index = -1; 1375 int char_index = -1; 1376 PDFiumPage::Area area = GetCharIndex(event, &page_index, &char_index, NULL); 1377 if (!selecting_) { 1378 PP_CursorType_Dev cursor; 1379 switch (area) { 1380 case PDFiumPage::TEXT_AREA: 1381 cursor = PP_CURSORTYPE_IBEAM; 1382 break; 1383 case PDFiumPage::WEBLINK_AREA: 1384 case PDFiumPage::DOCLINK_AREA: 1385 cursor = PP_CURSORTYPE_HAND; 1386 break; 1387 case PDFiumPage::NONSELECTABLE_AREA: 1388 default: 1389 cursor = PP_CURSORTYPE_POINTER; 1390 break; 1391 } 1392 1393 if (page_index != -1) { 1394 double page_x, page_y; 1395 pp::Point point = event.GetPosition(); 1396 DeviceToPage(page_index, point.x(), point.y(), &page_x, &page_y); 1397 1398 FORM_OnMouseMove(form_, pages_[page_index]->GetPage(), 0, page_x, page_y); 1399 int control = FPDPage_HasFormFieldAtPoint( 1400 form_, pages_[page_index]->GetPage(), page_x, page_y); 1401 switch (control) { 1402 case FPDF_FORMFIELD_PUSHBUTTON: 1403 case FPDF_FORMFIELD_CHECKBOX: 1404 case FPDF_FORMFIELD_RADIOBUTTON: 1405 case FPDF_FORMFIELD_COMBOBOX: 1406 case FPDF_FORMFIELD_LISTBOX: 1407 cursor = PP_CURSORTYPE_HAND; 1408 break; 1409 case FPDF_FORMFIELD_TEXTFIELD: 1410 cursor = PP_CURSORTYPE_IBEAM; 1411 break; 1412 default: 1413 break; 1414 } 1415 } 1416 1417 client_->UpdateCursor(cursor); 1418 pp::Point point = event.GetPosition(); 1419 std::string url = GetLinkAtPosition(event.GetPosition()); 1420 if (url != link_under_cursor_) { 1421 link_under_cursor_ = url; 1422 pp::PDF::SetLinkUnderCursor(GetPluginInstance(), url.c_str()); 1423 } 1424 // No need to swallow the event, since this might interfere with the 1425 // scrollbars if the user is dragging them. 1426 return false; 1427 } 1428 1429 // We're selecting but right now we're not over text, so don't change the 1430 // current selection. 1431 if (area != PDFiumPage::TEXT_AREA && area != PDFiumPage::WEBLINK_AREA && 1432 area != PDFiumPage::DOCLINK_AREA) { 1433 return false; 1434 } 1435 1436 SelectionChangeInvalidator selection_invalidator(this); 1437 1438 // Check if the user has descreased their selection area and we need to remove 1439 // pages from selection_. 1440 for (size_t i = 0; i < selection_.size(); ++i) { 1441 if (selection_[i].page_index() == page_index) { 1442 // There should be no other pages after this. 1443 selection_.erase(selection_.begin() + i + 1, selection_.end()); 1444 break; 1445 } 1446 } 1447 1448 if (selection_.size() == 0) 1449 return false; 1450 1451 int last = selection_.size() - 1; 1452 if (selection_[last].page_index() == page_index) { 1453 // Selecting within a page. 1454 int count; 1455 if (char_index >= selection_[last].char_index()) { 1456 // Selecting forward. 1457 count = char_index - selection_[last].char_index() + 1; 1458 } else { 1459 count = char_index - selection_[last].char_index() - 1; 1460 } 1461 selection_[last].SetCharCount(count); 1462 } else if (selection_[last].page_index() < page_index) { 1463 // Selecting into the next page. 1464 1465 // First make sure that there are no gaps in selection, i.e. if mousedown on 1466 // page one but we only get mousemove over page three, we want page two. 1467 for (int i = selection_[last].page_index() + 1; i < page_index; ++i) { 1468 selection_.push_back(PDFiumRange(pages_[i], 0, 1469 pages_[i]->GetCharCount())); 1470 } 1471 1472 int count = pages_[selection_[last].page_index()]->GetCharCount(); 1473 selection_[last].SetCharCount(count - selection_[last].char_index()); 1474 selection_.push_back(PDFiumRange(pages_[page_index], 0, char_index)); 1475 } else { 1476 // Selecting into the previous page. 1477 selection_[last].SetCharCount(-selection_[last].char_index()); 1478 1479 // First make sure that there are no gaps in selection, i.e. if mousedown on 1480 // page three but we only get mousemove over page one, we want page two. 1481 for (int i = selection_[last].page_index() - 1; i > page_index; --i) { 1482 selection_.push_back(PDFiumRange(pages_[i], 0, 1483 pages_[i]->GetCharCount())); 1484 } 1485 1486 int count = pages_[page_index]->GetCharCount(); 1487 selection_.push_back( 1488 PDFiumRange(pages_[page_index], count, count - char_index)); 1489 } 1490 1491 return true; 1492 } 1493 1494 bool PDFiumEngine::OnKeyDown(const pp::KeyboardInputEvent& event) { 1495 if (last_page_mouse_down_ == -1) 1496 return false; 1497 1498 bool rv = !!FORM_OnKeyDown( 1499 form_, pages_[last_page_mouse_down_]->GetPage(), 1500 event.GetKeyCode(), event.GetModifiers()); 1501 1502 if (event.GetKeyCode() == ui::VKEY_BACK || 1503 event.GetKeyCode() == ui::VKEY_ESCAPE) { 1504 // Chrome doesn't send char events for backspace or escape keys, see 1505 // PlatformKeyboardEventBuilder::isCharacterKey() and 1506 // http://chrome-corpsvn.mtv.corp.google.com/viewvc?view=rev&root=chrome&revision=31805 1507 // for more information. So just fake one since PDFium uses it. 1508 std::string str; 1509 str.push_back(event.GetKeyCode()); 1510 pp::KeyboardInputEvent synthesized(pp::KeyboardInputEvent( 1511 client_->GetPluginInstance(), 1512 PP_INPUTEVENT_TYPE_CHAR, 1513 event.GetTimeStamp(), 1514 event.GetModifiers(), 1515 event.GetKeyCode(), 1516 str)); 1517 OnChar(synthesized); 1518 } 1519 1520 return rv; 1521 } 1522 1523 bool PDFiumEngine::OnKeyUp(const pp::KeyboardInputEvent& event) { 1524 if (last_page_mouse_down_ == -1) 1525 return false; 1526 1527 return !!FORM_OnKeyUp( 1528 form_, pages_[last_page_mouse_down_]->GetPage(), 1529 event.GetKeyCode(), event.GetModifiers()); 1530 } 1531 1532 bool PDFiumEngine::OnChar(const pp::KeyboardInputEvent& event) { 1533 if (last_page_mouse_down_ == -1) 1534 return false; 1535 1536 base::string16 str = base::UTF8ToUTF16(event.GetCharacterText().AsString()); 1537 return !!FORM_OnChar( 1538 form_, pages_[last_page_mouse_down_]->GetPage(), 1539 str[0], 1540 event.GetModifiers()); 1541 } 1542 1543 void PDFiumEngine::StartFind(const char* text, bool case_sensitive) { 1544 // We can get a call to StartFind before we have any page information (i.e. 1545 // before the first call to LoadDocument has happened). Handle this case. 1546 if (pages_.empty()) 1547 return; 1548 1549 bool first_search = false; 1550 int character_to_start_searching_from = 0; 1551 if (current_find_text_ != text) { // First time we search for this text. 1552 first_search = true; 1553 std::vector<PDFiumRange> old_selection = selection_; 1554 StopFind(); 1555 current_find_text_ = text; 1556 1557 if (old_selection.empty()) { 1558 // Start searching from the beginning of the document. 1559 next_page_to_search_ = 0; 1560 last_page_to_search_ = pages_.size() - 1; 1561 last_character_index_to_search_ = -1; 1562 } else { 1563 // There's a current selection, so start from it. 1564 next_page_to_search_ = old_selection[0].page_index(); 1565 last_character_index_to_search_ = old_selection[0].char_index(); 1566 character_to_start_searching_from = old_selection[0].char_index(); 1567 last_page_to_search_ = next_page_to_search_; 1568 } 1569 } 1570 1571 int current_page = next_page_to_search_; 1572 1573 if (pages_[current_page]->available()) { 1574 base::string16 str = base::UTF8ToUTF16(text); 1575 // Don't use PDFium to search for now, since it doesn't support unicode text. 1576 // Leave the code for now to avoid bit-rot, in case it's fixed later. 1577 if (0) { 1578 SearchUsingPDFium( 1579 str, case_sensitive, first_search, character_to_start_searching_from, 1580 current_page); 1581 } else { 1582 SearchUsingICU( 1583 str, case_sensitive, first_search, character_to_start_searching_from, 1584 current_page); 1585 } 1586 1587 if (!IsPageVisible(current_page)) 1588 pages_[current_page]->Unload(); 1589 } 1590 1591 if (next_page_to_search_ != last_page_to_search_ || 1592 (first_search && last_character_index_to_search_ != -1)) { 1593 ++next_page_to_search_; 1594 } 1595 1596 if (next_page_to_search_ == static_cast<int>(pages_.size())) 1597 next_page_to_search_ = 0; 1598 // If there's only one page in the document and we start searching midway, 1599 // then we'll want to search the page one more time. 1600 bool end_of_search = 1601 next_page_to_search_ == last_page_to_search_ && 1602 // Only one page but didn't start midway. 1603 ((pages_.size() == 1 && last_character_index_to_search_ == -1) || 1604 // Started midway, but only 1 page and we already looped around. 1605 (pages_.size() == 1 && !first_search) || 1606 // Started midway, and we've just looped around. 1607 (pages_.size() > 1 && current_page == next_page_to_search_)); 1608 1609 if (end_of_search) { 1610 // Send the final notification. 1611 client_->NotifyNumberOfFindResultsChanged(find_results_.size(), true); 1612 } else { 1613 pp::CompletionCallback callback = 1614 find_factory_.NewCallback(&PDFiumEngine::ContinueFind); 1615 pp::Module::Get()->core()->CallOnMainThread( 1616 0, callback, case_sensitive ? 1 : 0); 1617 } 1618 } 1619 1620 void PDFiumEngine::SearchUsingPDFium(const base::string16& term, 1621 bool case_sensitive, 1622 bool first_search, 1623 int character_to_start_searching_from, 1624 int current_page) { 1625 // Find all the matches in the current page. 1626 unsigned long flags = case_sensitive ? FPDF_MATCHCASE : 0; 1627 FPDF_SCHHANDLE find = FPDFText_FindStart( 1628 pages_[current_page]->GetTextPage(), 1629 reinterpret_cast<const unsigned short*>(term.c_str()), 1630 flags, character_to_start_searching_from); 1631 1632 // Note: since we search one page at a time, we don't find matches across 1633 // page boundaries. We could do this manually ourself, but it seems low 1634 // priority since Reader itself doesn't do it. 1635 while (FPDFText_FindNext(find)) { 1636 PDFiumRange result(pages_[current_page], 1637 FPDFText_GetSchResultIndex(find), 1638 FPDFText_GetSchCount(find)); 1639 1640 if (!first_search && 1641 last_character_index_to_search_ != -1 && 1642 result.page_index() == last_page_to_search_ && 1643 result.char_index() >= last_character_index_to_search_) { 1644 break; 1645 } 1646 1647 AddFindResult(result); 1648 } 1649 1650 FPDFText_FindClose(find); 1651 } 1652 1653 void PDFiumEngine::SearchUsingICU(const base::string16& term, 1654 bool case_sensitive, 1655 bool first_search, 1656 int character_to_start_searching_from, 1657 int current_page) { 1658 base::string16 page_text; 1659 int text_length = pages_[current_page]->GetCharCount(); 1660 if (character_to_start_searching_from) { 1661 text_length -= character_to_start_searching_from; 1662 } else if (!first_search && 1663 last_character_index_to_search_ != -1 && 1664 current_page == last_page_to_search_) { 1665 text_length = last_character_index_to_search_; 1666 } 1667 if (text_length <= 0) 1668 return; 1669 unsigned short* data = 1670 reinterpret_cast<unsigned short*>(WriteInto(&page_text, text_length + 1)); 1671 FPDFText_GetText(pages_[current_page]->GetTextPage(), 1672 character_to_start_searching_from, 1673 text_length, 1674 data); 1675 std::vector<PDFEngine::Client::SearchStringResult> results; 1676 client_->SearchString( 1677 page_text.c_str(), term.c_str(), case_sensitive, &results); 1678 for (size_t i = 0; i < results.size(); ++i) { 1679 // Need to map the indexes from the page text, which may have generated 1680 // characters like space etc, to character indices from the page. 1681 int temp_start = results[i].start_index + character_to_start_searching_from; 1682 int start = FPDFText_GetCharIndexFromTextIndex( 1683 pages_[current_page]->GetTextPage(), temp_start); 1684 int end = FPDFText_GetCharIndexFromTextIndex( 1685 pages_[current_page]->GetTextPage(), 1686 temp_start + results[i].length); 1687 AddFindResult(PDFiumRange(pages_[current_page], start, end - start)); 1688 } 1689 } 1690 1691 void PDFiumEngine::AddFindResult(const PDFiumRange& result) { 1692 // Figure out where to insert the new location, since we could have 1693 // started searching midway and now we wrapped. 1694 size_t i; 1695 int page_index = result.page_index(); 1696 int char_index = result.char_index(); 1697 for (i = 0; i < find_results_.size(); ++i) { 1698 if (find_results_[i].page_index() > page_index || 1699 (find_results_[i].page_index() == page_index && 1700 find_results_[i].char_index() > char_index)) { 1701 break; 1702 } 1703 } 1704 find_results_.insert(find_results_.begin() + i, result); 1705 UpdateTickMarks(); 1706 1707 if (current_find_index_ == -1) { 1708 // Select the first match. 1709 SelectFindResult(true); 1710 } else if (static_cast<int>(i) <= current_find_index_) { 1711 // Update the current match index 1712 current_find_index_++; 1713 client_->NotifySelectedFindResultChanged(current_find_index_); 1714 } 1715 client_->NotifyNumberOfFindResultsChanged(find_results_.size(), false); 1716 } 1717 1718 bool PDFiumEngine::SelectFindResult(bool forward) { 1719 if (find_results_.empty()) { 1720 NOTREACHED(); 1721 return false; 1722 } 1723 1724 SelectionChangeInvalidator selection_invalidator(this); 1725 1726 // Move back/forward through the search locations we previously found. 1727 if (forward) { 1728 if (++current_find_index_ == static_cast<int>(find_results_.size())) 1729 current_find_index_ = 0; 1730 } else { 1731 if (--current_find_index_ < 0) { 1732 current_find_index_ = find_results_.size() - 1; 1733 } 1734 } 1735 1736 // Update the selection before telling the client to scroll, since it could 1737 // paint then. 1738 selection_.clear(); 1739 selection_.push_back(find_results_[current_find_index_]); 1740 1741 // If the result is not in view, scroll to it. 1742 size_t i; 1743 pp::Rect bounding_rect; 1744 pp::Rect visible_rect = GetVisibleRect(); 1745 // Use zoom of 1.0 since visible_rect is without zoom. 1746 std::vector<pp::Rect> rects = find_results_[current_find_index_]. 1747 GetScreenRects(pp::Point(), 1.0, current_rotation_); 1748 for (i = 0; i < rects.size(); ++i) 1749 bounding_rect = bounding_rect.Union(rects[i]); 1750 if (!visible_rect.Contains(bounding_rect)) { 1751 pp::Point center = bounding_rect.CenterPoint(); 1752 // Make the page centered. 1753 int new_y = static_cast<int>(center.y() * current_zoom_) - 1754 static_cast<int>(visible_rect.height() * current_zoom_ / 2); 1755 if (new_y < 0) 1756 new_y = 0; 1757 client_->ScrollToY(new_y); 1758 1759 // Only move horizontally if it's not visible. 1760 if (center.x() < visible_rect.x() || center.x() > visible_rect.right()) { 1761 int new_x = static_cast<int>(center.x() * current_zoom_) - 1762 static_cast<int>(visible_rect.width() * current_zoom_ / 2); 1763 if (new_x < 0) 1764 new_x = 0; 1765 client_->ScrollToX(new_x); 1766 } 1767 } 1768 1769 client_->NotifySelectedFindResultChanged(current_find_index_); 1770 1771 return true; 1772 } 1773 1774 void PDFiumEngine::StopFind() { 1775 SelectionChangeInvalidator selection_invalidator(this); 1776 1777 selection_.clear(); 1778 selecting_ = false; 1779 find_results_.clear(); 1780 next_page_to_search_ = -1; 1781 last_page_to_search_ = -1; 1782 last_character_index_to_search_ = -1; 1783 current_find_index_ = -1; 1784 current_find_text_.clear(); 1785 UpdateTickMarks(); 1786 find_factory_.CancelAll(); 1787 } 1788 1789 void PDFiumEngine::UpdateTickMarks() { 1790 std::vector<pp::Rect> tickmarks; 1791 for (size_t i = 0; i < find_results_.size(); ++i) { 1792 pp::Rect rect; 1793 // Always use an origin of 0,0 since scroll positions don't affect tickmark. 1794 std::vector<pp::Rect> rects = find_results_[i].GetScreenRects( 1795 pp::Point(0, 0), current_zoom_, current_rotation_); 1796 for (size_t j = 0; j < rects.size(); ++j) 1797 rect = rect.Union(rects[j]); 1798 tickmarks.push_back(rect); 1799 } 1800 1801 client_->UpdateTickMarks(tickmarks); 1802 } 1803 1804 void PDFiumEngine::ZoomUpdated(double new_zoom_level) { 1805 CancelPaints(); 1806 1807 current_zoom_ = new_zoom_level; 1808 1809 CalculateVisiblePages(); 1810 UpdateTickMarks(); 1811 } 1812 1813 void PDFiumEngine::RotateClockwise() { 1814 current_rotation_ = (current_rotation_ + 1) % 4; 1815 InvalidateAllPages(); 1816 } 1817 1818 void PDFiumEngine::RotateCounterclockwise() { 1819 current_rotation_ = (current_rotation_ - 1) % 4; 1820 InvalidateAllPages(); 1821 } 1822 1823 void PDFiumEngine::InvalidateAllPages() { 1824 selection_.clear(); 1825 find_results_.clear(); 1826 1827 CancelPaints(); 1828 LoadPageInfo(true); 1829 UpdateTickMarks(); 1830 client_->Invalidate(pp::Rect(plugin_size_)); 1831 } 1832 1833 std::string PDFiumEngine::GetSelectedText() { 1834 base::string16 result; 1835 for (size_t i = 0; i < selection_.size(); ++i) { 1836 if (i > 0 && 1837 selection_[i - 1].page_index() > selection_[i].page_index()) { 1838 result = selection_[i].GetText() + result; 1839 } else { 1840 result.append(selection_[i].GetText()); 1841 } 1842 } 1843 1844 return base::UTF16ToUTF8(result); 1845 } 1846 1847 std::string PDFiumEngine::GetLinkAtPosition(const pp::Point& point) { 1848 int temp; 1849 PDFiumPage::LinkTarget target; 1850 PDFiumPage::Area area = GetCharIndex(point, &temp, &temp, &target); 1851 if (area == PDFiumPage::WEBLINK_AREA) 1852 return target.url; 1853 return std::string(); 1854 } 1855 1856 bool PDFiumEngine::IsSelecting() { 1857 return selecting_; 1858 } 1859 1860 bool PDFiumEngine::HasPermission(DocumentPermission permission) const { 1861 switch (permission) { 1862 case PERMISSION_COPY: 1863 return (permissions_ & kPDFPermissionCopyMask) != 0; 1864 case PERMISSION_COPY_ACCESSIBLE: 1865 return (permissions_ & kPDFPermissionCopyAccessibleMask) != 0; 1866 case PERMISSION_PRINT_LOW_QUALITY: 1867 return (permissions_ & kPDFPermissionPrintLowQualityMask) != 0; 1868 case PERMISSION_PRINT_HIGH_QUALITY: 1869 return (permissions_ & kPDFPermissionPrintLowQualityMask) != 0 && 1870 (permissions_ & kPDFPermissionPrintHighQualityMask) != 0; 1871 default: 1872 return true; 1873 }; 1874 } 1875 1876 void PDFiumEngine::SelectAll() { 1877 SelectionChangeInvalidator selection_invalidator(this); 1878 1879 selection_.clear(); 1880 for (size_t i = 0; i < pages_.size(); ++i) 1881 if (pages_[i]->available()) { 1882 selection_.push_back(PDFiumRange(pages_[i], 0, 1883 pages_[i]->GetCharCount())); 1884 } 1885 } 1886 1887 int PDFiumEngine::GetNumberOfPages() { 1888 return pages_.size(); 1889 } 1890 1891 int PDFiumEngine::GetNamedDestinationPage(const std::string& destination) { 1892 // Look for the destination. 1893 FPDF_DEST dest = FPDF_GetNamedDestByName(doc_, destination.c_str()); 1894 if (!dest) { 1895 // Look for a bookmark with the same name. 1896 base::string16 destination_wide = base::UTF8ToUTF16(destination); 1897 FPDF_WIDESTRING destination_pdf_wide = 1898 reinterpret_cast<FPDF_WIDESTRING>(destination_wide.c_str()); 1899 FPDF_BOOKMARK bookmark = FPDFBookmark_Find(doc_, destination_pdf_wide); 1900 if (!bookmark) 1901 return -1; 1902 dest = FPDFBookmark_GetDest(doc_, bookmark); 1903 } 1904 return dest ? FPDFDest_GetPageIndex(doc_, dest) : -1; 1905 } 1906 1907 int PDFiumEngine::GetFirstVisiblePage() { 1908 CalculateVisiblePages(); 1909 return first_visible_page_; 1910 } 1911 1912 int PDFiumEngine::GetMostVisiblePage() { 1913 CalculateVisiblePages(); 1914 return most_visible_page_; 1915 } 1916 1917 pp::Rect PDFiumEngine::GetPageRect(int index) { 1918 pp::Rect rc(pages_[index]->rect()); 1919 rc.Inset(-kPageShadowLeft, -kPageShadowTop, 1920 -kPageShadowRight, -kPageShadowBottom); 1921 return rc; 1922 } 1923 1924 pp::Rect PDFiumEngine::GetPageContentsRect(int index) { 1925 return GetScreenRect(pages_[index]->rect()); 1926 } 1927 1928 void PDFiumEngine::PaintThumbnail(pp::ImageData* image_data, int index) { 1929 FPDF_BITMAP bitmap = FPDFBitmap_CreateEx( 1930 image_data->size().width(), image_data->size().height(), 1931 FPDFBitmap_BGRx, image_data->data(), image_data->stride()); 1932 1933 if (pages_[index]->available()) { 1934 FPDFBitmap_FillRect( 1935 bitmap, 0, 0, image_data->size().width(), image_data->size().height(), 1936 255, 255, 255, 255); 1937 1938 FPDF_RenderPageBitmap( 1939 bitmap, pages_[index]->GetPage(), 0, 0, image_data->size().width(), 1940 image_data->size().height(), 0, GetRenderingFlags()); 1941 } else { 1942 FPDFBitmap_FillRect( 1943 bitmap, 0, 0, image_data->size().width(), image_data->size().height(), 1944 kPendingPageColorR, kPendingPageColorG, kPendingPageColorB, 1945 kPendingPageColorA); 1946 } 1947 1948 FPDFBitmap_Destroy(bitmap); 1949 } 1950 1951 void PDFiumEngine::SetGrayscale(bool grayscale) { 1952 render_grayscale_ = grayscale; 1953 } 1954 1955 void PDFiumEngine::OnCallback(int id) { 1956 if (!timers_.count(id)) 1957 return; 1958 1959 timers_[id].second(id); 1960 if (timers_.count(id)) // The callback might delete the timer. 1961 client_->ScheduleCallback(id, timers_[id].first); 1962 } 1963 1964 std::string PDFiumEngine::GetPageAsJSON(int index) { 1965 if (!(HasPermission(PERMISSION_COPY) || 1966 HasPermission(PERMISSION_COPY_ACCESSIBLE))) { 1967 return "{}"; 1968 } 1969 1970 if (index < 0 || static_cast<size_t>(index) > pages_.size() - 1) 1971 return "{}"; 1972 1973 scoped_ptr<base::Value> node( 1974 pages_[index]->GetAccessibleContentAsValue(current_rotation_)); 1975 std::string page_json; 1976 base::JSONWriter::Write(node.get(), &page_json); 1977 return page_json; 1978 } 1979 1980 bool PDFiumEngine::GetPrintScaling() { 1981 return !!FPDF_VIEWERREF_GetPrintScaling(doc_); 1982 } 1983 1984 void PDFiumEngine::AppendBlankPages(int num_pages) { 1985 DCHECK(num_pages != 0); 1986 1987 if (!doc_) 1988 return; 1989 1990 selection_.clear(); 1991 pending_pages_.clear(); 1992 1993 // Delete all pages except the first one. 1994 while (pages_.size() > 1) { 1995 delete pages_.back(); 1996 pages_.pop_back(); 1997 FPDFPage_Delete(doc_, pages_.size()); 1998 } 1999 2000 // Calculate document size and all page sizes. 2001 std::vector<pp::Rect> page_rects; 2002 pp::Size page_size = GetPageSize(0); 2003 page_size.Enlarge(kPageShadowLeft + kPageShadowRight, 2004 kPageShadowTop + kPageShadowBottom); 2005 pp::Size old_document_size = document_size_; 2006 document_size_ = pp::Size(page_size.width(), 0); 2007 for (int i = 0; i < num_pages; ++i) { 2008 if (i != 0) { 2009 // Add space for horizontal separator. 2010 document_size_.Enlarge(0, kPageSeparatorThickness); 2011 } 2012 2013 pp::Rect rect(pp::Point(0, document_size_.height()), page_size); 2014 page_rects.push_back(rect); 2015 2016 document_size_.Enlarge(0, page_size.height()); 2017 } 2018 2019 // Create blank pages. 2020 for (int i = 1; i < num_pages; ++i) { 2021 pp::Rect page_rect(page_rects[i]); 2022 page_rect.Inset(kPageShadowLeft, kPageShadowTop, 2023 kPageShadowRight, kPageShadowBottom); 2024 double width_in_points = 2025 page_rect.width() * kPointsPerInch / kPixelsPerInch; 2026 double height_in_points = 2027 page_rect.height() * kPointsPerInch / kPixelsPerInch; 2028 FPDFPage_New(doc_, i, width_in_points, height_in_points); 2029 pages_.push_back(new PDFiumPage(this, i, page_rect, true)); 2030 } 2031 2032 CalculateVisiblePages(); 2033 if (document_size_ != old_document_size) 2034 client_->DocumentSizeUpdated(document_size_); 2035 } 2036 2037 void PDFiumEngine::LoadDocument() { 2038 // Check if the document is ready for loading. If it isn't just bail for now, 2039 // we will call LoadDocument() again later. 2040 if (!doc_ && !doc_loader_.IsDocumentComplete() && 2041 !FPDFAvail_IsDocAvail(fpdf_availability_, &download_hints_)) { 2042 return; 2043 } 2044 2045 // If we're in the middle of getting a password, just return. We will retry 2046 // loading the document after we get the password anyway. 2047 if (getting_password_) 2048 return; 2049 2050 ScopedUnsupportedFeature scoped_unsupported_feature(this); 2051 bool needs_password = false; 2052 if (TryLoadingDoc(false, std::string(), &needs_password)) { 2053 ContinueLoadingDocument(false, std::string()); 2054 return; 2055 } 2056 if (needs_password) 2057 GetPasswordAndLoad(); 2058 else 2059 client_->DocumentLoadFailed(); 2060 } 2061 2062 bool PDFiumEngine::TryLoadingDoc(bool with_password, 2063 const std::string& password, 2064 bool* needs_password) { 2065 *needs_password = false; 2066 if (doc_) 2067 return true; 2068 2069 const char* password_cstr = NULL; 2070 if (with_password) { 2071 password_cstr = password.c_str(); 2072 password_tries_remaining_--; 2073 } 2074 if (doc_loader_.IsDocumentComplete()) 2075 doc_ = FPDF_LoadCustomDocument(&file_access_, password_cstr); 2076 else 2077 doc_ = FPDFAvail_GetDocument(fpdf_availability_, password_cstr); 2078 2079 if (!doc_ && FPDF_GetLastError() == FPDF_ERR_PASSWORD) 2080 *needs_password = true; 2081 2082 return doc_ != NULL; 2083 } 2084 2085 void PDFiumEngine::GetPasswordAndLoad() { 2086 getting_password_ = true; 2087 DCHECK(!doc_ && FPDF_GetLastError() == FPDF_ERR_PASSWORD); 2088 client_->GetDocumentPassword(password_factory_.NewCallbackWithOutput( 2089 &PDFiumEngine::OnGetPasswordComplete)); 2090 } 2091 2092 void PDFiumEngine::OnGetPasswordComplete(int32_t result, 2093 const pp::Var& password) { 2094 getting_password_ = false; 2095 2096 bool password_given = false; 2097 std::string password_text; 2098 if (result == PP_OK && password.is_string()) { 2099 password_text = password.AsString(); 2100 if (!password_text.empty()) 2101 password_given = true; 2102 } 2103 ContinueLoadingDocument(password_given, password_text); 2104 } 2105 2106 void PDFiumEngine::ContinueLoadingDocument( 2107 bool has_password, 2108 const std::string& password) { 2109 ScopedUnsupportedFeature scoped_unsupported_feature(this); 2110 2111 bool needs_password = false; 2112 bool loaded = TryLoadingDoc(has_password, password, &needs_password); 2113 bool password_incorrect = !loaded && has_password && needs_password; 2114 if (password_incorrect && password_tries_remaining_ > 0) { 2115 GetPasswordAndLoad(); 2116 return; 2117 } 2118 2119 if (!doc_) { 2120 client_->DocumentLoadFailed(); 2121 return; 2122 } 2123 2124 if (FPDFDoc_GetPageMode(doc_) == PAGEMODE_USEOUTLINES) 2125 client_->DocumentHasUnsupportedFeature("Bookmarks"); 2126 2127 permissions_ = FPDF_GetDocPermissions(doc_); 2128 2129 if (!form_) { 2130 // Only returns 0 when data isn't available. If form data is downloaded, or 2131 // if this isn't a form, returns positive values. 2132 if (!doc_loader_.IsDocumentComplete() && 2133 !FPDFAvail_IsFormAvail(fpdf_availability_, &download_hints_)) { 2134 return; 2135 } 2136 2137 form_ = FPDFDOC_InitFormFillEnviroument( 2138 doc_, static_cast<FPDF_FORMFILLINFO*>(this)); 2139 FPDF_SetFormFieldHighlightColor(form_, 0, kFormHighlightColor); 2140 FPDF_SetFormFieldHighlightAlpha(form_, kFormHighlightAlpha); 2141 } 2142 2143 if (!doc_loader_.IsDocumentComplete()) { 2144 // Check if the first page is available. In a linearized PDF, that is not 2145 // always page 0. Doing this gives us the default page size, since when the 2146 // document is available, the first page is available as well. 2147 CheckPageAvailable(FPDFAvail_GetFirstPageNum(doc_), &pending_pages_); 2148 } 2149 2150 LoadPageInfo(false); 2151 2152 if (doc_loader_.IsDocumentComplete()) 2153 FinishLoadingDocument(); 2154 } 2155 2156 void PDFiumEngine::LoadPageInfo(bool reload) { 2157 pending_pages_.clear(); 2158 pp::Size old_document_size = document_size_; 2159 document_size_ = pp::Size(); 2160 std::vector<pp::Rect> page_rects; 2161 int page_count = FPDF_GetPageCount(doc_); 2162 bool doc_complete = doc_loader_.IsDocumentComplete(); 2163 for (int i = 0; i < page_count; ++i) { 2164 if (i != 0) { 2165 // Add space for horizontal separator. 2166 document_size_.Enlarge(0, kPageSeparatorThickness); 2167 } 2168 2169 // Get page availability. If reload==false, and document is not loaded yet 2170 // (we are using async loading) - mark all pages as unavailable. 2171 // If reload==true (we have document constructed already), get page 2172 // availability flag from already existing PDFiumPage class. 2173 bool page_available = reload ? pages_[i]->available() : doc_complete; 2174 2175 pp::Size size = page_available ? GetPageSize(i) : default_page_size_; 2176 size.Enlarge(kPageShadowLeft + kPageShadowRight, 2177 kPageShadowTop + kPageShadowBottom); 2178 pp::Rect rect(pp::Point(0, document_size_.height()), size); 2179 page_rects.push_back(rect); 2180 2181 if (size.width() > document_size_.width()) 2182 document_size_.set_width(size.width()); 2183 2184 document_size_.Enlarge(0, size.height()); 2185 } 2186 2187 for (int i = 0; i < page_count; ++i) { 2188 // Center pages relative to the entire document. 2189 page_rects[i].set_x((document_size_.width() - page_rects[i].width()) / 2); 2190 pp::Rect page_rect(page_rects[i]); 2191 page_rect.Inset(kPageShadowLeft, kPageShadowTop, 2192 kPageShadowRight, kPageShadowBottom); 2193 if (reload) { 2194 pages_[i]->set_rect(page_rect); 2195 } else { 2196 pages_.push_back(new PDFiumPage(this, i, page_rect, doc_complete)); 2197 } 2198 } 2199 2200 CalculateVisiblePages(); 2201 if (document_size_ != old_document_size) 2202 client_->DocumentSizeUpdated(document_size_); 2203 } 2204 2205 void PDFiumEngine::CalculateVisiblePages() { 2206 // Clear pending requests queue, since it may contain requests to the pages 2207 // that are already invisible (after scrolling for example). 2208 pending_pages_.clear(); 2209 doc_loader_.ClearPendingRequests(); 2210 2211 visible_pages_.clear(); 2212 pp::Rect visible_rect(plugin_size_); 2213 for (size_t i = 0; i < pages_.size(); ++i) { 2214 // Check an entire PageScreenRect, since we might need to repaint side 2215 // borders and shadows even if the page itself is not visible. 2216 // For example, when user use pdf with different page sizes and zoomed in 2217 // outside page area. 2218 if (visible_rect.Intersects(GetPageScreenRect(i))) { 2219 visible_pages_.push_back(i); 2220 CheckPageAvailable(i, &pending_pages_); 2221 } else { 2222 // Need to unload pages when we're not using them, since some PDFs use a 2223 // lot of memory. See http://crbug.com/48791 2224 if (defer_page_unload_) { 2225 deferred_page_unloads_.push_back(i); 2226 } else { 2227 pages_[i]->Unload(); 2228 } 2229 2230 // If the last mouse down was on a page that's no longer visible, reset 2231 // that variable so that we don't send keyboard events to it (the focus 2232 // will be lost when the page is first closed anyways). 2233 if (static_cast<int>(i) == last_page_mouse_down_) 2234 last_page_mouse_down_ = -1; 2235 } 2236 } 2237 2238 // Any pending highlighting of form fields will be invalid since these are in 2239 // screen coordinates. 2240 form_highlights_.clear(); 2241 2242 if (visible_pages_.size() == 0) 2243 first_visible_page_ = -1; 2244 else 2245 first_visible_page_ = visible_pages_.front(); 2246 2247 int most_visible_page = first_visible_page_; 2248 // Check if the next page is more visible than the first one. 2249 if (most_visible_page != -1 && 2250 pages_.size() > 0 && 2251 most_visible_page < static_cast<int>(pages_.size()) - 1) { 2252 pp::Rect rc_first = 2253 visible_rect.Intersect(GetPageScreenRect(most_visible_page)); 2254 pp::Rect rc_next = 2255 visible_rect.Intersect(GetPageScreenRect(most_visible_page + 1)); 2256 if (rc_next.height() > rc_first.height()) 2257 most_visible_page++; 2258 } 2259 2260 SetCurrentPage(most_visible_page); 2261 } 2262 2263 bool PDFiumEngine::IsPageVisible(int index) const { 2264 for (size_t i = 0; i < visible_pages_.size(); ++i) { 2265 if (visible_pages_[i] == index) 2266 return true; 2267 } 2268 2269 return false; 2270 } 2271 2272 bool PDFiumEngine::CheckPageAvailable(int index, std::vector<int>* pending) { 2273 if (!doc_ || !form_) 2274 return false; 2275 2276 if (static_cast<int>(pages_.size()) > index && pages_[index]->available()) 2277 return true; 2278 2279 if (!FPDFAvail_IsPageAvail(fpdf_availability_, index, &download_hints_)) { 2280 size_t j; 2281 for (j = 0; j < pending->size(); ++j) { 2282 if ((*pending)[j] == index) 2283 break; 2284 } 2285 2286 if (j == pending->size()) 2287 pending->push_back(index); 2288 return false; 2289 } 2290 2291 if (static_cast<int>(pages_.size()) > index) 2292 pages_[index]->set_available(true); 2293 if (!default_page_size_.GetArea()) 2294 default_page_size_ = GetPageSize(index); 2295 return true; 2296 } 2297 2298 pp::Size PDFiumEngine::GetPageSize(int index) { 2299 pp::Size size; 2300 double width_in_points = 0; 2301 double height_in_points = 0; 2302 int rv = FPDF_GetPageSizeByIndex( 2303 doc_, index, &width_in_points, &height_in_points); 2304 2305 if (rv) { 2306 int width_in_pixels = static_cast<int>( 2307 width_in_points * kPixelsPerInch / kPointsPerInch); 2308 int height_in_pixels = static_cast<int>( 2309 height_in_points * kPixelsPerInch / kPointsPerInch); 2310 if (current_rotation_ % 2 == 1) 2311 std::swap(width_in_pixels, height_in_pixels); 2312 size = pp::Size(width_in_pixels, height_in_pixels); 2313 } 2314 return size; 2315 } 2316 2317 int PDFiumEngine::StartPaint(int page_index, const pp::Rect& dirty) { 2318 // For the first time we hit paint, do nothing and just record the paint for 2319 // the next callback. This keeps the UI responsive in case the user is doing 2320 // a lot of scrolling. 2321 ProgressivePaint progressive; 2322 progressive.rect = dirty; 2323 progressive.page_index = page_index; 2324 progressive.bitmap = NULL; 2325 progressive.painted_ = false; 2326 progressive_paints_.push_back(progressive); 2327 return progressive_paints_.size() - 1; 2328 } 2329 2330 bool PDFiumEngine::ContinuePaint(int progressive_index, 2331 pp::ImageData* image_data) { 2332 #if defined(OS_LINUX) 2333 g_last_instance_id = client_->GetPluginInstance()->pp_instance(); 2334 #endif 2335 2336 int rv; 2337 int page_index = progressive_paints_[progressive_index].page_index; 2338 last_progressive_start_time_ = base::Time::Now(); 2339 if (progressive_paints_[progressive_index].bitmap) { 2340 rv = FPDF_RenderPage_Continue( 2341 pages_[page_index]->GetPage(), static_cast<IFSDK_PAUSE*>(this)); 2342 } else { 2343 pp::Rect dirty = progressive_paints_[progressive_index].rect; 2344 progressive_paints_[progressive_index].bitmap = CreateBitmap(dirty, 2345 image_data); 2346 int start_x, start_y, size_x, size_y; 2347 GetPDFiumRect( 2348 page_index, dirty, &start_x, &start_y, &size_x, &size_y); 2349 FPDFBitmap_FillRect( 2350 progressive_paints_[progressive_index].bitmap, start_x, start_y, size_x, 2351 size_y, 255, 255, 255, 255); 2352 rv = FPDF_RenderPageBitmap_Start( 2353 progressive_paints_[progressive_index].bitmap, 2354 pages_[page_index]->GetPage(), start_x, start_y, size_x, size_y, 2355 current_rotation_, 2356 GetRenderingFlags(), static_cast<IFSDK_PAUSE*>(this)); 2357 } 2358 return rv != FPDF_RENDER_TOBECOUNTINUED; 2359 } 2360 2361 void PDFiumEngine::FinishPaint(int progressive_index, 2362 pp::ImageData* image_data) { 2363 int page_index = progressive_paints_[progressive_index].page_index; 2364 pp::Rect dirty_in_screen = progressive_paints_[progressive_index].rect; 2365 FPDF_BITMAP bitmap = progressive_paints_[progressive_index].bitmap; 2366 int start_x, start_y, size_x, size_y; 2367 GetPDFiumRect( 2368 page_index, dirty_in_screen, &start_x, &start_y, &size_x, &size_y); 2369 2370 // Draw the forms. 2371 FPDF_FFLDraw( 2372 form_, bitmap, pages_[page_index]->GetPage(), start_x, start_y, size_x, 2373 size_y, current_rotation_, GetRenderingFlags()); 2374 2375 FillPageSides(progressive_index); 2376 2377 // Paint the page shadows. 2378 PaintPageShadow(progressive_index, image_data); 2379 2380 DrawSelections(progressive_index, image_data); 2381 2382 FPDF_RenderPage_Close(pages_[page_index]->GetPage()); 2383 FPDFBitmap_Destroy(bitmap); 2384 progressive_paints_.erase(progressive_paints_.begin() + progressive_index); 2385 2386 client_->DocumentPaintOccurred(); 2387 } 2388 2389 void PDFiumEngine::CancelPaints() { 2390 for (size_t i = 0; i < progressive_paints_.size(); ++i) { 2391 FPDF_RenderPage_Close(pages_[progressive_paints_[i].page_index]->GetPage()); 2392 FPDFBitmap_Destroy(progressive_paints_[i].bitmap); 2393 } 2394 progressive_paints_.clear(); 2395 } 2396 2397 void PDFiumEngine::FillPageSides(int progressive_index) { 2398 int page_index = progressive_paints_[progressive_index].page_index; 2399 pp::Rect dirty_in_screen = progressive_paints_[progressive_index].rect; 2400 FPDF_BITMAP bitmap = progressive_paints_[progressive_index].bitmap; 2401 2402 pp::Rect page_rect = pages_[page_index]->rect(); 2403 if (page_rect.x() > 0) { 2404 pp::Rect left(0, 2405 page_rect.y() - kPageShadowTop, 2406 page_rect.x() - kPageShadowLeft, 2407 page_rect.height() + kPageShadowTop + 2408 kPageShadowBottom + kPageSeparatorThickness); 2409 left = GetScreenRect(left).Intersect(dirty_in_screen); 2410 2411 FPDFBitmap_FillRect( 2412 bitmap, left.x() - dirty_in_screen.x(), 2413 left.y() - dirty_in_screen.y(), left.width(), left.height(), 2414 kBackgroundColorR, kBackgroundColorG, kBackgroundColorB, 2415 kBackgroundColorA); 2416 } 2417 2418 if (page_rect.right() < document_size_.width()) { 2419 pp::Rect right(page_rect.right() + kPageShadowRight, 2420 page_rect.y() - kPageShadowTop, 2421 document_size_.width() - page_rect.right() - 2422 kPageShadowRight, 2423 page_rect.height() + kPageShadowTop + 2424 kPageShadowBottom + kPageSeparatorThickness); 2425 right = GetScreenRect(right).Intersect(dirty_in_screen); 2426 2427 FPDFBitmap_FillRect( 2428 bitmap, right.x() - dirty_in_screen.x(), 2429 right.y() - dirty_in_screen.y(), right.width(), right.height(), 2430 kBackgroundColorR, kBackgroundColorG, kBackgroundColorB, 2431 kBackgroundColorA); 2432 } 2433 2434 // Paint separator. 2435 pp::Rect bottom(page_rect.x() - kPageShadowLeft, 2436 page_rect.bottom() + kPageShadowBottom, 2437 page_rect.width() + kPageShadowLeft + kPageShadowRight, 2438 kPageSeparatorThickness); 2439 bottom = GetScreenRect(bottom).Intersect(dirty_in_screen); 2440 2441 FPDFBitmap_FillRect( 2442 bitmap, bottom.x() - dirty_in_screen.x(), 2443 bottom.y() - dirty_in_screen.y(), bottom.width(), bottom.height(), 2444 kBackgroundColorR, kBackgroundColorG, kBackgroundColorB, 2445 kBackgroundColorA); 2446 } 2447 2448 void PDFiumEngine::PaintPageShadow(int progressive_index, 2449 pp::ImageData* image_data) { 2450 int page_index = progressive_paints_[progressive_index].page_index; 2451 pp::Rect dirty_in_screen = progressive_paints_[progressive_index].rect; 2452 pp::Rect page_rect = pages_[page_index]->rect(); 2453 pp::Rect shadow_rect(page_rect); 2454 shadow_rect.Inset(-kPageShadowLeft, -kPageShadowTop, 2455 -kPageShadowRight, -kPageShadowBottom); 2456 2457 // Due to the rounding errors of the GetScreenRect it is possible to get 2458 // different size shadows on the left and right sides even they are defined 2459 // the same. To fix this issue let's calculate shadow rect and then shrink 2460 // it by the size of the shadows. 2461 shadow_rect = GetScreenRect(shadow_rect); 2462 page_rect = shadow_rect; 2463 2464 page_rect.Inset(static_cast<int>(ceil(kPageShadowLeft * current_zoom_)), 2465 static_cast<int>(ceil(kPageShadowTop * current_zoom_)), 2466 static_cast<int>(ceil(kPageShadowRight * current_zoom_)), 2467 static_cast<int>(ceil(kPageShadowBottom * current_zoom_))); 2468 2469 DrawPageShadow(page_rect, shadow_rect, dirty_in_screen, image_data); 2470 } 2471 2472 void PDFiumEngine::DrawSelections(int progressive_index, 2473 pp::ImageData* image_data) { 2474 int page_index = progressive_paints_[progressive_index].page_index; 2475 pp::Rect dirty_in_screen = progressive_paints_[progressive_index].rect; 2476 2477 void* region = NULL; 2478 int stride; 2479 GetRegion(dirty_in_screen.point(), image_data, ®ion, &stride); 2480 2481 std::vector<pp::Rect> highlighted_rects; 2482 pp::Rect visible_rect = GetVisibleRect(); 2483 for (size_t k = 0; k < selection_.size(); ++k) { 2484 if (selection_[k].page_index() != page_index) 2485 continue; 2486 std::vector<pp::Rect> rects = selection_[k].GetScreenRects( 2487 visible_rect.point(), current_zoom_, current_rotation_); 2488 for (size_t j = 0; j < rects.size(); ++j) { 2489 pp::Rect visible_selection = rects[j].Intersect(dirty_in_screen); 2490 if (visible_selection.IsEmpty()) 2491 continue; 2492 2493 visible_selection.Offset( 2494 -dirty_in_screen.point().x(), -dirty_in_screen.point().y()); 2495 Highlight(region, stride, visible_selection, &highlighted_rects); 2496 } 2497 } 2498 2499 for (size_t k = 0; k < form_highlights_.size(); ++k) { 2500 pp::Rect visible_selection = form_highlights_[k].Intersect(dirty_in_screen); 2501 if (visible_selection.IsEmpty()) 2502 continue; 2503 2504 visible_selection.Offset( 2505 -dirty_in_screen.point().x(), -dirty_in_screen.point().y()); 2506 Highlight(region, stride, visible_selection, &highlighted_rects); 2507 } 2508 form_highlights_.clear(); 2509 } 2510 2511 void PDFiumEngine::PaintUnavailablePage(int page_index, 2512 const pp::Rect& dirty, 2513 pp::ImageData* image_data) { 2514 int start_x, start_y, size_x, size_y; 2515 GetPDFiumRect(page_index, dirty, &start_x, &start_y, &size_x, &size_y); 2516 FPDF_BITMAP bitmap = CreateBitmap(dirty, image_data); 2517 FPDFBitmap_FillRect(bitmap, start_x, start_y, size_x, size_y, 2518 kPendingPageColorR, kPendingPageColorG, kPendingPageColorB, 2519 kPendingPageColorA); 2520 2521 pp::Rect loading_text_in_screen( 2522 pages_[page_index]->rect().width() / 2, 2523 pages_[page_index]->rect().y() + kLoadingTextVerticalOffset, 0, 0); 2524 loading_text_in_screen = GetScreenRect(loading_text_in_screen); 2525 FPDFBitmap_Destroy(bitmap); 2526 } 2527 2528 int PDFiumEngine::GetProgressiveIndex(int page_index) const { 2529 for (size_t i = 0; i < progressive_paints_.size(); ++i) { 2530 if (progressive_paints_[i].page_index == page_index) 2531 return i; 2532 } 2533 return -1; 2534 } 2535 2536 FPDF_BITMAP PDFiumEngine::CreateBitmap(const pp::Rect& rect, 2537 pp::ImageData* image_data) const { 2538 void* region; 2539 int stride; 2540 GetRegion(rect.point(), image_data, ®ion, &stride); 2541 if (!region) 2542 return NULL; 2543 return FPDFBitmap_CreateEx( 2544 rect.width(), rect.height(), FPDFBitmap_BGRx, region, stride); 2545 } 2546 2547 void PDFiumEngine::GetPDFiumRect( 2548 int page_index, const pp::Rect& rect, int* start_x, int* start_y, 2549 int* size_x, int* size_y) const { 2550 pp::Rect page_rect = GetScreenRect(pages_[page_index]->rect()); 2551 page_rect.Offset(-rect.x(), -rect.y()); 2552 2553 *start_x = page_rect.x(); 2554 *start_y = page_rect.y(); 2555 *size_x = page_rect.width(); 2556 *size_y = page_rect.height(); 2557 } 2558 2559 int PDFiumEngine::GetRenderingFlags() const { 2560 int flags = FPDF_LCD_TEXT | FPDF_NO_CATCH; 2561 if (render_grayscale_) 2562 flags |= FPDF_GRAYSCALE; 2563 if (client_->IsPrintPreview()) 2564 flags |= FPDF_PRINTING; 2565 return flags; 2566 } 2567 2568 pp::Rect PDFiumEngine::GetVisibleRect() const { 2569 pp::Rect rv; 2570 rv.set_x(static_cast<int>(position_.x() / current_zoom_)); 2571 rv.set_y(static_cast<int>(position_.y() / current_zoom_)); 2572 rv.set_width(static_cast<int>(ceil(plugin_size_.width() / current_zoom_))); 2573 rv.set_height(static_cast<int>(ceil(plugin_size_.height() / current_zoom_))); 2574 return rv; 2575 } 2576 2577 pp::Rect PDFiumEngine::GetPageScreenRect(int page_index) const { 2578 // Since we use this rect for creating the PDFium bitmap, also include other 2579 // areas around the page that we might need to update such as the page 2580 // separator and the sides if the page is narrower than the document. 2581 return GetScreenRect(pp::Rect( 2582 0, 2583 pages_[page_index]->rect().y() - kPageShadowTop, 2584 document_size_.width(), 2585 pages_[page_index]->rect().height() + kPageShadowTop + 2586 kPageShadowBottom + kPageSeparatorThickness)); 2587 } 2588 2589 pp::Rect PDFiumEngine::GetScreenRect(const pp::Rect& rect) const { 2590 pp::Rect rv; 2591 int right = 2592 static_cast<int>(ceil(rect.right() * current_zoom_ - position_.x())); 2593 int bottom = 2594 static_cast<int>(ceil(rect.bottom() * current_zoom_ - position_.y())); 2595 2596 rv.set_x(static_cast<int>(rect.x() * current_zoom_ - position_.x())); 2597 rv.set_y(static_cast<int>(rect.y() * current_zoom_ - position_.y())); 2598 rv.set_width(right - rv.x()); 2599 rv.set_height(bottom - rv.y()); 2600 return rv; 2601 } 2602 2603 void PDFiumEngine::Highlight(void* buffer, 2604 int stride, 2605 const pp::Rect& rect, 2606 std::vector<pp::Rect>* highlighted_rects) { 2607 if (!buffer) 2608 return; 2609 2610 pp::Rect new_rect = rect; 2611 for (size_t i = 0; i < highlighted_rects->size(); ++i) 2612 new_rect = new_rect.Subtract((*highlighted_rects)[i]); 2613 2614 highlighted_rects->push_back(new_rect); 2615 int l = new_rect.x(); 2616 int t = new_rect.y(); 2617 int w = new_rect.width(); 2618 int h = new_rect.height(); 2619 2620 for (int y = t; y < t + h; ++y) { 2621 for (int x = l; x < l + w; ++x) { 2622 uint8* pixel = static_cast<uint8*>(buffer) + y * stride + x * 4; 2623 // This is our highlight color. 2624 pixel[0] = static_cast<uint8>( 2625 pixel[0] * (kHighlightColorB / 255.0)); 2626 pixel[1] = static_cast<uint8>( 2627 pixel[1] * (kHighlightColorG / 255.0)); 2628 pixel[2] = static_cast<uint8>( 2629 pixel[2] * (kHighlightColorR / 255.0)); 2630 } 2631 } 2632 } 2633 2634 PDFiumEngine::SelectionChangeInvalidator::SelectionChangeInvalidator( 2635 PDFiumEngine* engine) : engine_(engine) { 2636 previous_origin_ = engine_->GetVisibleRect().point(); 2637 GetVisibleSelectionsScreenRects(&old_selections_); 2638 } 2639 2640 PDFiumEngine::SelectionChangeInvalidator::~SelectionChangeInvalidator() { 2641 // Offset the old selections if the document scrolled since we recorded them. 2642 pp::Point offset = previous_origin_ - engine_->GetVisibleRect().point(); 2643 for (size_t i = 0; i < old_selections_.size(); ++i) 2644 old_selections_[i].Offset(offset); 2645 2646 std::vector<pp::Rect> new_selections; 2647 GetVisibleSelectionsScreenRects(&new_selections); 2648 for (size_t i = 0; i < new_selections.size(); ++i) { 2649 for (size_t j = 0; j < old_selections_.size(); ++j) { 2650 if (new_selections[i] == old_selections_[j]) { 2651 // Rectangle was selected before and after, so no need to invalidate it. 2652 // Mark the rectangles by setting them to empty. 2653 new_selections[i] = old_selections_[j] = pp::Rect(); 2654 } 2655 } 2656 } 2657 2658 for (size_t i = 0; i < old_selections_.size(); ++i) { 2659 if (!old_selections_[i].IsEmpty()) 2660 engine_->client_->Invalidate(old_selections_[i]); 2661 } 2662 for (size_t i = 0; i < new_selections.size(); ++i) { 2663 if (!new_selections[i].IsEmpty()) 2664 engine_->client_->Invalidate(new_selections[i]); 2665 } 2666 engine_->OnSelectionChanged(); 2667 } 2668 2669 void 2670 PDFiumEngine::SelectionChangeInvalidator::GetVisibleSelectionsScreenRects( 2671 std::vector<pp::Rect>* rects) { 2672 pp::Rect visible_rect = engine_->GetVisibleRect(); 2673 for (size_t i = 0; i < engine_->selection_.size(); ++i) { 2674 int page_index = engine_->selection_[i].page_index(); 2675 if (!engine_->IsPageVisible(page_index)) 2676 continue; // This selection is on a page that's not currently visible. 2677 2678 std::vector<pp::Rect> selection_rects = 2679 engine_->selection_[i].GetScreenRects( 2680 visible_rect.point(), 2681 engine_->current_zoom_, 2682 engine_->current_rotation_); 2683 rects->insert(rects->end(), selection_rects.begin(), selection_rects.end()); 2684 } 2685 } 2686 2687 void PDFiumEngine::DeviceToPage(int page_index, 2688 float device_x, 2689 float device_y, 2690 double* page_x, 2691 double* page_y) { 2692 *page_x = *page_y = 0; 2693 int temp_x = static_cast<int>((device_x + position_.x())/ current_zoom_ - 2694 pages_[page_index]->rect().x()); 2695 int temp_y = static_cast<int>((device_y + position_.y())/ current_zoom_ - 2696 pages_[page_index]->rect().y()); 2697 FPDF_DeviceToPage( 2698 pages_[page_index]->GetPage(), 0, 0, 2699 pages_[page_index]->rect().width(), pages_[page_index]->rect().height(), 2700 current_rotation_, temp_x, temp_y, page_x, page_y); 2701 } 2702 2703 int PDFiumEngine::GetVisiblePageIndex(FPDF_PAGE page) { 2704 for (size_t i = 0; i < visible_pages_.size(); ++i) { 2705 if (pages_[visible_pages_[i]]->GetPage() == page) 2706 return visible_pages_[i]; 2707 } 2708 return -1; 2709 } 2710 2711 void PDFiumEngine::SetCurrentPage(int index) { 2712 if (index == most_visible_page_ || !form_) 2713 return; 2714 if (most_visible_page_ != -1 && called_do_document_action_) { 2715 FPDF_PAGE old_page = pages_[most_visible_page_]->GetPage(); 2716 FORM_DoPageAAction(old_page, form_, FPDFPAGE_AACTION_CLOSE); 2717 } 2718 most_visible_page_ = index; 2719 #if defined(OS_LINUX) 2720 g_last_instance_id = client_->GetPluginInstance()->pp_instance(); 2721 #endif 2722 if (most_visible_page_ != -1 && called_do_document_action_) { 2723 FPDF_PAGE new_page = pages_[most_visible_page_]->GetPage(); 2724 FORM_DoPageAAction(new_page, form_, FPDFPAGE_AACTION_OPEN); 2725 } 2726 } 2727 2728 void PDFiumEngine::TransformPDFPageForPrinting( 2729 FPDF_PAGE page, 2730 const PP_PrintSettings_Dev& print_settings) { 2731 // Get the source page width and height in points. 2732 const double src_page_width = FPDF_GetPageWidth(page); 2733 const double src_page_height = FPDF_GetPageHeight(page); 2734 2735 const int src_page_rotation = FPDFPage_GetRotation(page); 2736 const bool fit_to_page = print_settings.print_scaling_option == 2737 PP_PRINTSCALINGOPTION_FIT_TO_PRINTABLE_AREA; 2738 2739 pp::Size page_size(print_settings.paper_size); 2740 pp::Rect content_rect(print_settings.printable_area); 2741 const bool rotated = (src_page_rotation % 2 == 1); 2742 SetPageSizeAndContentRect(rotated, 2743 src_page_width > src_page_height, 2744 &page_size, 2745 &content_rect); 2746 2747 // Compute the screen page width and height in points. 2748 const int actual_page_width = 2749 rotated ? page_size.height() : page_size.width(); 2750 const int actual_page_height = 2751 rotated ? page_size.width() : page_size.height(); 2752 2753 const double scale_factor = CalculateScaleFactor(fit_to_page, content_rect, 2754 src_page_width, 2755 src_page_height, rotated); 2756 2757 // Calculate positions for the clip box. 2758 ClipBox source_clip_box; 2759 CalculateClipBoxBoundary(page, scale_factor, rotated, &source_clip_box); 2760 2761 // Calculate the translation offset values. 2762 double offset_x = 0; 2763 double offset_y = 0; 2764 if (fit_to_page) { 2765 CalculateScaledClipBoxOffset(content_rect, source_clip_box, &offset_x, 2766 &offset_y); 2767 } else { 2768 CalculateNonScaledClipBoxOffset(content_rect, src_page_rotation, 2769 actual_page_width, actual_page_height, 2770 source_clip_box, &offset_x, &offset_y); 2771 } 2772 2773 // Reset the media box and crop box. When the page has crop box and media box, 2774 // the plugin will display the crop box contents and not the entire media box. 2775 // If the pages have different crop box values, the plugin will display a 2776 // document of multiple page sizes. To give better user experience, we 2777 // decided to have same crop box and media box values. Hence, the user will 2778 // see a list of uniform pages. 2779 FPDFPage_SetMediaBox(page, 0, 0, page_size.width(), page_size.height()); 2780 FPDFPage_SetCropBox(page, 0, 0, page_size.width(), page_size.height()); 2781 2782 // Transformation is not required, return. Do this check only after updating 2783 // the media box and crop box. For more detailed information, please refer to 2784 // the comment block right before FPDF_SetMediaBox and FPDF_GetMediaBox calls. 2785 if (scale_factor == 1.0 && offset_x == 0 && offset_y == 0) 2786 return; 2787 2788 2789 // All the positions have been calculated, now manipulate the PDF. 2790 FS_MATRIX matrix = {static_cast<float>(scale_factor), 2791 0, 2792 0, 2793 static_cast<float>(scale_factor), 2794 static_cast<float>(offset_x), 2795 static_cast<float>(offset_y)}; 2796 FS_RECTF cliprect = {static_cast<float>(source_clip_box.left+offset_x), 2797 static_cast<float>(source_clip_box.top+offset_y), 2798 static_cast<float>(source_clip_box.right+offset_x), 2799 static_cast<float>(source_clip_box.bottom+offset_y)}; 2800 FPDFPage_TransFormWithClip(page, &matrix, &cliprect); 2801 FPDFPage_TransformAnnots(page, scale_factor, 0, 0, scale_factor, 2802 offset_x, offset_y); 2803 } 2804 2805 void PDFiumEngine::DrawPageShadow(const pp::Rect& page_rc, 2806 const pp::Rect& shadow_rc, 2807 const pp::Rect& clip_rc, 2808 pp::ImageData* image_data) { 2809 pp::Rect page_rect(page_rc); 2810 page_rect.Offset(page_offset_); 2811 2812 pp::Rect shadow_rect(shadow_rc); 2813 shadow_rect.Offset(page_offset_); 2814 2815 pp::Rect clip_rect(clip_rc); 2816 clip_rect.Offset(page_offset_); 2817 2818 // Page drop shadow parameters. 2819 const double factor = 0.5; 2820 const uint32 background = (kBackgroundColorA << 24) | 2821 (kBackgroundColorR << 16) | 2822 (kBackgroundColorG << 8) | 2823 kBackgroundColorB; 2824 uint32 depth = std::max( 2825 std::max(page_rect.x() - shadow_rect.x(), 2826 page_rect.y() - shadow_rect.y()), 2827 std::max(shadow_rect.right() - page_rect.right(), 2828 shadow_rect.bottom() - page_rect.bottom())); 2829 depth = static_cast<uint32>(depth * 1.5) + 1; 2830 2831 // We need to check depth only to verify our copy of shadow matrix is correct. 2832 if (!page_shadow_.get() || page_shadow_->depth() != depth) 2833 page_shadow_.reset(new ShadowMatrix(depth, factor, background)); 2834 2835 DCHECK(!image_data->is_null()); 2836 DrawShadow(image_data, shadow_rect, page_rect, clip_rect, *page_shadow_); 2837 } 2838 2839 void PDFiumEngine::GetRegion(const pp::Point& location, 2840 pp::ImageData* image_data, 2841 void** region, 2842 int* stride) const { 2843 if (image_data->is_null()) { 2844 DCHECK(plugin_size_.IsEmpty()); 2845 *stride = 0; 2846 *region = NULL; 2847 return; 2848 } 2849 char* buffer = static_cast<char*>(image_data->data()); 2850 *stride = image_data->stride(); 2851 2852 pp::Point offset_location = location + page_offset_; 2853 // TODO: update this when we support BIDI and scrollbars can be on the left. 2854 if (!buffer || 2855 !pp::Rect(page_offset_, plugin_size_).Contains(offset_location)) { 2856 *region = NULL; 2857 return; 2858 } 2859 2860 buffer += location.y() * (*stride); 2861 buffer += (location.x() + page_offset_.x()) * 4; 2862 *region = buffer; 2863 } 2864 2865 void PDFiumEngine::OnSelectionChanged() { 2866 if (HasPermission(PDFEngine::PERMISSION_COPY)) 2867 pp::PDF::SetSelectedText(GetPluginInstance(), GetSelectedText().c_str()); 2868 } 2869 2870 void PDFiumEngine::Form_Invalidate(FPDF_FORMFILLINFO* param, 2871 FPDF_PAGE page, 2872 double left, 2873 double top, 2874 double right, 2875 double bottom) { 2876 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 2877 int page_index = engine->GetVisiblePageIndex(page); 2878 if (page_index == -1) { 2879 // This can sometime happen when the page is closed because it went off 2880 // screen, and PDFium invalidates the control as it's being deleted. 2881 return; 2882 } 2883 2884 pp::Rect rect = engine->pages_[page_index]->PageToScreen( 2885 engine->GetVisibleRect().point(), engine->current_zoom_, left, top, right, 2886 bottom, engine->current_rotation_); 2887 engine->client_->Invalidate(rect); 2888 } 2889 2890 void PDFiumEngine::Form_OutputSelectedRect(FPDF_FORMFILLINFO* param, 2891 FPDF_PAGE page, 2892 double left, 2893 double top, 2894 double right, 2895 double bottom) { 2896 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 2897 int page_index = engine->GetVisiblePageIndex(page); 2898 if (page_index == -1) { 2899 NOTREACHED(); 2900 return; 2901 } 2902 pp::Rect rect = engine->pages_[page_index]->PageToScreen( 2903 engine->GetVisibleRect().point(), engine->current_zoom_, left, top, right, 2904 bottom, engine->current_rotation_); 2905 engine->form_highlights_.push_back(rect); 2906 } 2907 2908 void PDFiumEngine::Form_SetCursor(FPDF_FORMFILLINFO* param, int cursor_type) { 2909 // We don't need this since it's not enough to change the cursor in all 2910 // scenarios. Instead, we check which form field we're under in OnMouseMove. 2911 } 2912 2913 int PDFiumEngine::Form_SetTimer(FPDF_FORMFILLINFO* param, 2914 int elapse, 2915 TimerCallback timer_func) { 2916 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 2917 engine->timers_[++engine->next_timer_id_] = 2918 std::pair<int, TimerCallback>(elapse, timer_func); 2919 engine->client_->ScheduleCallback(engine->next_timer_id_, elapse); 2920 return engine->next_timer_id_; 2921 } 2922 2923 void PDFiumEngine::Form_KillTimer(FPDF_FORMFILLINFO* param, int timer_id) { 2924 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 2925 engine->timers_.erase(timer_id); 2926 } 2927 2928 FPDF_SYSTEMTIME PDFiumEngine::Form_GetLocalTime(FPDF_FORMFILLINFO* param) { 2929 base::Time time = base::Time::Now(); 2930 base::Time::Exploded exploded; 2931 time.LocalExplode(&exploded); 2932 2933 FPDF_SYSTEMTIME rv; 2934 rv.wYear = exploded.year; 2935 rv.wMonth = exploded.month; 2936 rv.wDayOfWeek = exploded.day_of_week; 2937 rv.wDay = exploded.day_of_month; 2938 rv.wHour = exploded.hour; 2939 rv.wMinute = exploded.minute; 2940 rv.wSecond = exploded.second; 2941 rv.wMilliseconds = exploded.millisecond; 2942 return rv; 2943 } 2944 2945 void PDFiumEngine::Form_OnChange(FPDF_FORMFILLINFO* param) { 2946 // Don't care about. 2947 } 2948 2949 FPDF_PAGE PDFiumEngine::Form_GetPage(FPDF_FORMFILLINFO* param, 2950 FPDF_DOCUMENT document, 2951 int page_index) { 2952 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 2953 if (page_index < 0 || page_index >= static_cast<int>(engine->pages_.size())) 2954 return NULL; 2955 return engine->pages_[page_index]->GetPage(); 2956 } 2957 2958 FPDF_PAGE PDFiumEngine::Form_GetCurrentPage(FPDF_FORMFILLINFO* param, 2959 FPDF_DOCUMENT document) { 2960 // TODO(jam): find out what this is used for. 2961 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 2962 int index = engine->last_page_mouse_down_; 2963 if (index == -1) { 2964 index = engine->GetMostVisiblePage(); 2965 if (index == -1) { 2966 NOTREACHED(); 2967 return NULL; 2968 } 2969 } 2970 2971 return engine->pages_[index]->GetPage(); 2972 } 2973 2974 int PDFiumEngine::Form_GetRotation(FPDF_FORMFILLINFO* param, FPDF_PAGE page) { 2975 return 0; 2976 } 2977 2978 void PDFiumEngine::Form_ExecuteNamedAction(FPDF_FORMFILLINFO* param, 2979 FPDF_BYTESTRING named_action) { 2980 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 2981 std::string action(named_action); 2982 if (action == "Print") { 2983 engine->client_->Print(); 2984 return; 2985 } 2986 2987 int index = engine->last_page_mouse_down_; 2988 /* Don't try to calculate the most visible page if we don't have a left click 2989 before this event (this code originally copied Form_GetCurrentPage which of 2990 course needs to do that and which doesn't have recursion). This can end up 2991 causing infinite recursion. See http://crbug.com/240413 for more 2992 information. Either way, it's not necessary for the spec'd list of named 2993 actions. 2994 if (index == -1) 2995 index = engine->GetMostVisiblePage(); 2996 */ 2997 if (index == -1) 2998 return; 2999 3000 // This is the only list of named actions per the spec (see 12.6.4.11). Adobe 3001 // Reader supports more, like FitWidth, but since they're not part of the spec 3002 // and we haven't got bugs about them, no need to now. 3003 if (action == "NextPage") { 3004 engine->client_->ScrollToPage(index + 1); 3005 } else if (action == "PrevPage") { 3006 engine->client_->ScrollToPage(index - 1); 3007 } else if (action == "FirstPage") { 3008 engine->client_->ScrollToPage(0); 3009 } else if (action == "LastPage") { 3010 engine->client_->ScrollToPage(engine->pages_.size() - 1); 3011 } 3012 } 3013 3014 void PDFiumEngine::Form_SetTextFieldFocus(FPDF_FORMFILLINFO* param, 3015 FPDF_WIDESTRING value, 3016 FPDF_DWORD valueLen, 3017 FPDF_BOOL is_focus) { 3018 // Do nothing for now. 3019 // TODO(gene): use this signal to trigger OSK. 3020 } 3021 3022 void PDFiumEngine::Form_DoURIAction(FPDF_FORMFILLINFO* param, 3023 FPDF_BYTESTRING uri) { 3024 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3025 engine->client_->NavigateTo(std::string(uri), false); 3026 } 3027 3028 void PDFiumEngine::Form_DoGoToAction(FPDF_FORMFILLINFO* param, 3029 int page_index, 3030 int zoom_mode, 3031 float* position_array, 3032 int size_of_array) { 3033 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3034 engine->client_->ScrollToPage(page_index); 3035 } 3036 3037 int PDFiumEngine::Form_Alert(IPDF_JSPLATFORM* param, 3038 FPDF_WIDESTRING message, 3039 FPDF_WIDESTRING title, 3040 int type, 3041 int icon) { 3042 // See fpdfformfill.h for these values. 3043 enum AlertType { 3044 ALERT_TYPE_OK = 0, 3045 ALERT_TYPE_OK_CANCEL, 3046 ALERT_TYPE_YES_ON, 3047 ALERT_TYPE_YES_NO_CANCEL 3048 }; 3049 3050 enum AlertResult { 3051 ALERT_RESULT_OK = 1, 3052 ALERT_RESULT_CANCEL, 3053 ALERT_RESULT_NO, 3054 ALERT_RESULT_YES 3055 }; 3056 3057 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3058 std::string message_str = 3059 base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(message)); 3060 if (type == ALERT_TYPE_OK) { 3061 engine->client_->Alert(message_str); 3062 return ALERT_RESULT_OK; 3063 } 3064 3065 bool rv = engine->client_->Confirm(message_str); 3066 if (type == ALERT_TYPE_OK_CANCEL) 3067 return rv ? ALERT_RESULT_OK : ALERT_RESULT_CANCEL; 3068 return rv ? ALERT_RESULT_YES : ALERT_RESULT_NO; 3069 } 3070 3071 void PDFiumEngine::Form_Beep(IPDF_JSPLATFORM* param, int type) { 3072 // Beeps are annoying, and not possible using javascript, so ignore for now. 3073 } 3074 3075 int PDFiumEngine::Form_Response(IPDF_JSPLATFORM* param, 3076 FPDF_WIDESTRING question, 3077 FPDF_WIDESTRING title, 3078 FPDF_WIDESTRING default_response, 3079 FPDF_WIDESTRING label, 3080 FPDF_BOOL password, 3081 void* response, 3082 int length) { 3083 std::string question_str = base::UTF16ToUTF8( 3084 reinterpret_cast<const base::char16*>(question)); 3085 std::string default_str = base::UTF16ToUTF8( 3086 reinterpret_cast<const base::char16*>(default_response)); 3087 3088 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3089 std::string rv = engine->client_->Prompt(question_str, default_str); 3090 base::string16 rv_16 = base::UTF8ToUTF16(rv); 3091 int rv_bytes = rv_16.size() * sizeof(base::char16); 3092 if (response && rv_bytes <= length) 3093 memcpy(response, rv_16.c_str(), rv_bytes); 3094 return rv_bytes; 3095 } 3096 3097 int PDFiumEngine::Form_GetFilePath(IPDF_JSPLATFORM* param, 3098 void* file_path, 3099 int length) { 3100 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3101 std::string rv = engine->client_->GetURL(); 3102 if (file_path && rv.size() <= static_cast<size_t>(length)) 3103 memcpy(file_path, rv.c_str(), rv.size()); 3104 return rv.size(); 3105 } 3106 3107 void PDFiumEngine::Form_Mail(IPDF_JSPLATFORM* param, 3108 void* mail_data, 3109 int length, 3110 FPDF_BOOL ui, 3111 FPDF_WIDESTRING to, 3112 FPDF_WIDESTRING subject, 3113 FPDF_WIDESTRING cc, 3114 FPDF_WIDESTRING bcc, 3115 FPDF_WIDESTRING message) { 3116 DCHECK(length == 0); // Don't handle attachments; no way with mailto. 3117 std::string to_str = 3118 base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(to)); 3119 std::string cc_str = 3120 base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(cc)); 3121 std::string bcc_str = 3122 base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(bcc)); 3123 std::string subject_str = 3124 base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(subject)); 3125 std::string message_str = 3126 base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(message)); 3127 3128 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3129 engine->client_->Email(to_str, cc_str, bcc_str, subject_str, message_str); 3130 } 3131 3132 void PDFiumEngine::Form_Print(IPDF_JSPLATFORM* param, 3133 FPDF_BOOL ui, 3134 int start, 3135 int end, 3136 FPDF_BOOL silent, 3137 FPDF_BOOL shrink_to_fit, 3138 FPDF_BOOL print_as_image, 3139 FPDF_BOOL reverse, 3140 FPDF_BOOL annotations) { 3141 // No way to pass the extra information to the print dialog using JavaScript. 3142 // Just opening it is fine for now. 3143 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3144 engine->client_->Print(); 3145 } 3146 3147 void PDFiumEngine::Form_SubmitForm(IPDF_JSPLATFORM* param, 3148 void* form_data, 3149 int length, 3150 FPDF_WIDESTRING url) { 3151 std::string url_str = 3152 base::UTF16ToUTF8(reinterpret_cast<const base::char16*>(url)); 3153 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3154 engine->client_->SubmitForm(url_str, form_data, length); 3155 } 3156 3157 void PDFiumEngine::Form_GotoPage(IPDF_JSPLATFORM* param, 3158 int page_number) { 3159 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3160 engine->client_->ScrollToPage(page_number); 3161 } 3162 3163 int PDFiumEngine::Form_Browse(IPDF_JSPLATFORM* param, 3164 void* file_path, 3165 int length) { 3166 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3167 std::string path = engine->client_->ShowFileSelectionDialog(); 3168 if (path.size() + 1 <= static_cast<size_t>(length)) 3169 memcpy(file_path, &path[0], path.size() + 1); 3170 return path.size() + 1; 3171 } 3172 3173 FPDF_BOOL PDFiumEngine::Pause_NeedToPauseNow(IFSDK_PAUSE* param) { 3174 PDFiumEngine* engine = static_cast<PDFiumEngine*>(param); 3175 return (base::Time::Now() - engine->last_progressive_start_time_). 3176 InMilliseconds() > engine->progressive_paint_timeout_; 3177 } 3178 3179 ScopedUnsupportedFeature::ScopedUnsupportedFeature(PDFiumEngine* engine) 3180 : engine_(engine), old_engine_(g_engine_for_unsupported) { 3181 g_engine_for_unsupported = engine_; 3182 } 3183 3184 ScopedUnsupportedFeature::~ScopedUnsupportedFeature() { 3185 g_engine_for_unsupported = old_engine_; 3186 } 3187 3188 PDFEngineExports* PDFEngineExports::Create() { 3189 return new PDFiumEngineExports; 3190 } 3191 3192 namespace { 3193 3194 int CalculatePosition(FPDF_PAGE page, 3195 const PDFiumEngineExports::RenderingSettings& settings, 3196 pp::Rect* dest) { 3197 int page_width = static_cast<int>( 3198 FPDF_GetPageWidth(page) * settings.dpi_x / kPointsPerInch); 3199 int page_height = static_cast<int>( 3200 FPDF_GetPageHeight(page) * settings.dpi_y / kPointsPerInch); 3201 3202 // Start by assuming that we will draw exactly to the bounds rect 3203 // specified. 3204 *dest = settings.bounds; 3205 3206 int rotate = 0; // normal orientation. 3207 3208 // Auto-rotate landscape pages to print correctly. 3209 if (settings.autorotate && 3210 (dest->width() > dest->height()) != (page_width > page_height)) { 3211 rotate = 1; // 90 degrees clockwise. 3212 std::swap(page_width, page_height); 3213 } 3214 3215 // See if we need to scale the output 3216 bool scale_to_bounds = false; 3217 if (settings.fit_to_bounds && 3218 ((page_width > dest->width()) || (page_height > dest->height()))) { 3219 scale_to_bounds = true; 3220 } else if (settings.stretch_to_bounds && 3221 ((page_width < dest->width()) || (page_height < dest->height()))) { 3222 scale_to_bounds = true; 3223 } 3224 3225 if (scale_to_bounds) { 3226 // If we need to maintain aspect ratio, calculate the actual width and 3227 // height. 3228 if (settings.keep_aspect_ratio) { 3229 double scale_factor_x = page_width; 3230 scale_factor_x /= dest->width(); 3231 double scale_factor_y = page_height; 3232 scale_factor_y /= dest->height(); 3233 if (scale_factor_x > scale_factor_y) { 3234 dest->set_height(page_height / scale_factor_x); 3235 } else { 3236 dest->set_width(page_width / scale_factor_y); 3237 } 3238 } 3239 } else { 3240 // We are not scaling to bounds. Draw in the actual page size. If the 3241 // actual page size is larger than the bounds, the output will be 3242 // clipped. 3243 dest->set_width(page_width); 3244 dest->set_height(page_height); 3245 } 3246 3247 if (settings.center_in_bounds) { 3248 pp::Point offset((settings.bounds.width() - dest->width()) / 2, 3249 (settings.bounds.height() - dest->height()) / 2); 3250 dest->Offset(offset); 3251 } 3252 return rotate; 3253 } 3254 3255 } // namespace 3256 3257 #if defined(OS_WIN) 3258 bool PDFiumEngineExports::RenderPDFPageToDC(const void* pdf_buffer, 3259 int buffer_size, 3260 int page_number, 3261 const RenderingSettings& settings, 3262 HDC dc) { 3263 FPDF_DOCUMENT doc = FPDF_LoadMemDocument(pdf_buffer, buffer_size, NULL); 3264 if (!doc) 3265 return false; 3266 FPDF_PAGE page = FPDF_LoadPage(doc, page_number); 3267 if (!page) { 3268 FPDF_CloseDocument(doc); 3269 return false; 3270 } 3271 RenderingSettings new_settings = settings; 3272 // calculate the page size 3273 if (new_settings.dpi_x == -1) 3274 new_settings.dpi_x = GetDeviceCaps(dc, LOGPIXELSX); 3275 if (new_settings.dpi_y == -1) 3276 new_settings.dpi_y = GetDeviceCaps(dc, LOGPIXELSY); 3277 3278 pp::Rect dest; 3279 int rotate = CalculatePosition(page, new_settings, &dest); 3280 3281 int save_state = SaveDC(dc); 3282 // The caller wanted all drawing to happen within the bounds specified. 3283 // Based on scale calculations, our destination rect might be larger 3284 // than the bounds. Set the clip rect to the bounds. 3285 IntersectClipRect(dc, settings.bounds.x(), settings.bounds.y(), 3286 settings.bounds.x() + settings.bounds.width(), 3287 settings.bounds.y() + settings.bounds.height()); 3288 3289 // A temporary hack. PDFs generated by Cairo (used by Chrome OS to generate 3290 // a PDF output from a webpage) result in very large metafiles and the 3291 // rendering using FPDF_RenderPage is incorrect. In this case, render as a 3292 // bitmap. Note that this code does not kick in for PDFs printed from Chrome 3293 // because in that case we create a temp PDF first before printing and this 3294 // temp PDF does not have a creator string that starts with "cairo". 3295 base::string16 creator; 3296 size_t buffer_bytes = FPDF_GetMetaText(doc, "Creator", NULL, 0); 3297 if (buffer_bytes > 1) { 3298 FPDF_GetMetaText(doc, "Creator", WriteInto(&creator, buffer_bytes), 3299 buffer_bytes); 3300 } 3301 bool use_bitmap = false; 3302 if (StartsWith(creator, L"cairo", false)) 3303 use_bitmap = true; 3304 3305 // Another temporary hack. Some PDFs seems to render very slowly if 3306 // FPDF_RenderPage is directly used on a printer DC. I suspect it is 3307 // because of the code to talk Postscript directly to the printer if 3308 // the printer supports this. Need to discuss this with PDFium. For now, 3309 // render to a bitmap and then blit the bitmap to the DC if we have been 3310 // supplied a printer DC. 3311 int device_type = GetDeviceCaps(dc, TECHNOLOGY); 3312 if (use_bitmap || 3313 (device_type == DT_RASPRINTER) || (device_type == DT_PLOTTER)) { 3314 FPDF_BITMAP bitmap = FPDFBitmap_Create(dest.width(), dest.height(), 3315 FPDFBitmap_BGRx); 3316 // Clear the bitmap 3317 FPDFBitmap_FillRect(bitmap, 0, 0, dest.width(), dest.height(), 255, 255, 3318 255, 255); 3319 FPDF_RenderPageBitmap( 3320 bitmap, page, 0, 0, dest.width(), dest.height(), rotate, 3321 FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH); 3322 int stride = FPDFBitmap_GetStride(bitmap); 3323 BITMAPINFO bmi; 3324 memset(&bmi, 0, sizeof(bmi)); 3325 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 3326 bmi.bmiHeader.biWidth = dest.width(); 3327 bmi.bmiHeader.biHeight = -dest.height(); // top-down image 3328 bmi.bmiHeader.biPlanes = 1; 3329 bmi.bmiHeader.biBitCount = 32; 3330 bmi.bmiHeader.biCompression = BI_RGB; 3331 bmi.bmiHeader.biSizeImage = stride * dest.height(); 3332 StretchDIBits(dc, dest.x(), dest.y(), dest.width(), dest.height(), 3333 0, 0, dest.width(), dest.height(), 3334 FPDFBitmap_GetBuffer(bitmap), &bmi, DIB_RGB_COLORS, SRCCOPY); 3335 FPDFBitmap_Destroy(bitmap); 3336 } else { 3337 FPDF_RenderPage(dc, page, dest.x(), dest.y(), dest.width(), dest.height(), 3338 rotate, FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH); 3339 } 3340 RestoreDC(dc, save_state); 3341 FPDF_ClosePage(page); 3342 FPDF_CloseDocument(doc); 3343 return true; 3344 } 3345 #endif // OS_WIN 3346 3347 bool PDFiumEngineExports::RenderPDFPageToBitmap( 3348 const void* pdf_buffer, 3349 int pdf_buffer_size, 3350 int page_number, 3351 const RenderingSettings& settings, 3352 void* bitmap_buffer) { 3353 FPDF_DOCUMENT doc = FPDF_LoadMemDocument(pdf_buffer, pdf_buffer_size, NULL); 3354 if (!doc) 3355 return false; 3356 FPDF_PAGE page = FPDF_LoadPage(doc, page_number); 3357 if (!page) { 3358 FPDF_CloseDocument(doc); 3359 return false; 3360 } 3361 3362 pp::Rect dest; 3363 int rotate = CalculatePosition(page, settings, &dest); 3364 3365 FPDF_BITMAP bitmap = 3366 FPDFBitmap_CreateEx(settings.bounds.width(), settings.bounds.height(), 3367 FPDFBitmap_BGRA, bitmap_buffer, 3368 settings.bounds.width() * 4); 3369 // Clear the bitmap 3370 FPDFBitmap_FillRect(bitmap, 0, 0, settings.bounds.width(), 3371 settings.bounds.height(), 255, 255, 255, 255); 3372 // Shift top-left corner of bounds to (0, 0) if it's not there. 3373 dest.set_point(dest.point() - settings.bounds.point()); 3374 FPDF_RenderPageBitmap( 3375 bitmap, page, dest.x(), dest.y(), dest.width(), dest.height(), rotate, 3376 FPDF_ANNOT | FPDF_PRINTING | FPDF_NO_CATCH); 3377 FPDFBitmap_Destroy(bitmap); 3378 FPDF_ClosePage(page); 3379 FPDF_CloseDocument(doc); 3380 return true; 3381 } 3382 3383 bool PDFiumEngineExports::GetPDFDocInfo(const void* pdf_buffer, 3384 int buffer_size, 3385 int* page_count, 3386 double* max_page_width) { 3387 FPDF_DOCUMENT doc = FPDF_LoadMemDocument(pdf_buffer, buffer_size, NULL); 3388 if (!doc) 3389 return false; 3390 int page_count_local = FPDF_GetPageCount(doc); 3391 if (page_count) { 3392 *page_count = page_count_local; 3393 } 3394 if (max_page_width) { 3395 *max_page_width = 0; 3396 for (int page_number = 0; page_number < page_count_local; page_number++) { 3397 double page_width = 0; 3398 double page_height = 0; 3399 FPDF_GetPageSizeByIndex(doc, page_number, &page_width, &page_height); 3400 if (page_width > *max_page_width) { 3401 *max_page_width = page_width; 3402 } 3403 } 3404 } 3405 FPDF_CloseDocument(doc); 3406 return true; 3407 } 3408 3409 } // namespace chrome_pdf 3410