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