Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(85)

Side by Side Diff: chrome/renderer/print_web_view_helper.cc

Issue 11359020: Print headers and footers with WebKit. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/renderer/print_web_view_helper.h" 5 #include "chrome/renderer/print_web_view_helper.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/auto_reset.h" 9 #include "base/auto_reset.h"
10 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/json/json_writer.h"
11 #include "base/logging.h" 12 #include "base/logging.h"
12 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
14 #include "base/process_util.h"
15 #include "base/stringprintf.h"
13 #include "base/string_number_conversions.h" 16 #include "base/string_number_conversions.h"
14 #include "base/utf_string_conversions.h" 17 #include "base/utf_string_conversions.h"
15 #include "chrome/common/chrome_switches.h" 18 #include "chrome/common/chrome_switches.h"
16 #include "chrome/common/print_messages.h" 19 #include "chrome/common/print_messages.h"
17 #include "chrome/common/render_messages.h" 20 #include "chrome/common/render_messages.h"
18 #include "chrome/common/url_constants.h"
19 #include "chrome/renderer/prerender/prerender_helper.h" 21 #include "chrome/renderer/prerender/prerender_helper.h"
20 #include "content/public/renderer/render_thread.h" 22 #include "content/public/renderer/render_thread.h"
21 #include "content/public/renderer/render_view.h" 23 #include "content/public/renderer/render_view.h"
24 #include "grit/browser_resources.h"
22 #include "grit/generated_resources.h" 25 #include "grit/generated_resources.h"
26 #include "printing/metafile.h"
23 #include "printing/metafile_impl.h" 27 #include "printing/metafile_impl.h"
24 #include "printing/page_size_margins.h"
25 #include "printing/print_job_constants.h"
26 #include "printing/units.h" 28 #include "printing/units.h"
27 #include "skia/ext/vector_canvas.h"
28 #include "skia/ext/vector_platform_device_skia.h" 29 #include "skia/ext/vector_platform_device_skia.h"
29 #include "third_party/skia/include/core/SkRect.h"
30 #include "third_party/skia/include/core/SkTypeface.h"
31 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCanvas.h"
32 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h" 30 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h"
33 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLReques t.h" 31 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLReques t.h"
34 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLRespon se.h"
35 #include "third_party/WebKit/Source/WebKit/chromium/public/WebConsoleMessage.h" 32 #include "third_party/WebKit/Source/WebKit/chromium/public/WebConsoleMessage.h"
36 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h"
37 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" 33 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
38 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" 34 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
39 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" 35 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
40 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPlugin.h" 36 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPlugin.h"
41 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginDocument.h" 37 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginDocument.h"
38 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPrintParams.h"
42 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPrintScalingOption .h" 39 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPrintScalingOption .h"
40 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScriptSource.h"
41 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSettings.h"
43 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" 42 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
44 #include "ui/base/l10n/l10n_util.h" 43 #include "ui/base/l10n/l10n_util.h"
45 #include "ui/base/layout.h" 44 #include "ui/base/resource/resource_bundle.h"
46 #include "ui/gfx/rect.h"
47 #include "ui/gfx/skia_util.h"
48 #include "webkit/glue/webpreferences.h" 45 #include "webkit/glue/webpreferences.h"
49 46
50 #if defined(OS_POSIX)
51 #include "base/process_util.h"
52 #endif
53
54 #if defined(OS_WIN) || defined(OS_MACOSX)
55 #define USE_RENDER_TEXT
56 #endif
57
58 #if defined(USE_RENDER_TEXT)
59 #include "ui/gfx/canvas.h"
60 #include "ui/gfx/render_text.h"
61 #endif
62
63 namespace { 47 namespace {
64 48
65 #if defined(USE_RENDER_TEXT) 49 const double kMinDpi = 1.0;
66 typedef gfx::RenderText* HeaderFooterPaint;
67 #else
68 typedef SkPaint HeaderFooterPaint;
69 #endif
70 50
71 const double kMinDpi = 1.0; 51 const char kPageLoadScriptFormat[] =
52 "document.open(); document.write(%s); document.close();";
53
54 const char kPageSetupScriptFormat[] = "setup(%s);";
55
56 void ExecuteScript(WebKit::WebFrame* frame,
57 const char* script_format,
58 const base::Value& parameters) {
59 std::string json;
60 base::JSONWriter::Write(&parameters, &json);
61 std::string script = StringPrintf(script_format, json.c_str());
62 frame->executeScript(WebKit::WebString(UTF8ToUTF16(script)));
63 }
72 64
73 int GetDPI(const PrintMsg_Print_Params* print_params) { 65 int GetDPI(const PrintMsg_Print_Params* print_params) {
74 #if defined(OS_MACOSX) 66 #if defined(OS_MACOSX)
75 // On the Mac, the printable area is in points, don't do any scaling based 67 // On the Mac, the printable area is in points, don't do any scaling based
76 // on dpi. 68 // on dpi.
77 return printing::kPointsPerInch; 69 return printing::kPointsPerInch;
78 #else 70 #else
79 return static_cast<int>(print_params->dpi); 71 return static_cast<int>(print_params->dpi);
80 #endif // defined(OS_MACOSX) 72 #endif // defined(OS_MACOSX)
81 } 73 }
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after
344 336
345 bool FitToPageEnabled(const DictionaryValue& job_settings) { 337 bool FitToPageEnabled(const DictionaryValue& job_settings) {
346 bool fit_to_paper_size = false; 338 bool fit_to_paper_size = false;
347 if (!job_settings.GetBoolean(printing::kSettingFitToPageEnabled, 339 if (!job_settings.GetBoolean(printing::kSettingFitToPageEnabled,
348 &fit_to_paper_size)) { 340 &fit_to_paper_size)) {
349 NOTREACHED(); 341 NOTREACHED();
350 } 342 }
351 return fit_to_paper_size; 343 return fit_to_paper_size;
352 } 344 }
353 345
354 // Get the (x, y) coordinate from where printing of the current text should
355 // start depending on the horizontal alignment (LEFT, RIGHT, CENTER) and
356 // vertical alignment (TOP, BOTTOM).
357 SkPoint GetHeaderFooterPosition(
358 float webkit_scale_factor,
359 const printing::PageSizeMargins& page_layout,
360 printing::HorizontalHeaderFooterPosition horizontal_position,
361 printing::VerticalHeaderFooterPosition vertical_position,
362 double offset_to_baseline,
363 double text_width_in_points) {
364 SkScalar x = 0;
365 switch (horizontal_position) {
366 case printing::LEFT: {
367 x = printing::kSettingHeaderFooterInterstice - page_layout.margin_left;
368 break;
369 }
370 case printing::RIGHT: {
371 x = page_layout.content_width + page_layout.margin_right -
372 printing::kSettingHeaderFooterInterstice - text_width_in_points;
373 break;
374 }
375 case printing::CENTER: {
376 SkScalar available_width = printing::GetHeaderFooterSegmentWidth(
377 page_layout.margin_left + page_layout.margin_right +
378 page_layout.content_width);
379 x = available_width - page_layout.margin_left +
380 (available_width - text_width_in_points) / 2;
381 break;
382 }
383 default: {
384 NOTREACHED();
385 }
386 }
387
388 SkScalar y = 0;
389 switch (vertical_position) {
390 case printing::TOP:
391 y = printing::kSettingHeaderFooterInterstice -
392 page_layout.margin_top - offset_to_baseline;
393 break;
394 case printing::BOTTOM:
395 y = page_layout.margin_bottom + page_layout.content_height -
396 printing::kSettingHeaderFooterInterstice - offset_to_baseline;
397 break;
398 default:
399 NOTREACHED();
400 }
401
402 SkPoint point = SkPoint::Make(x / webkit_scale_factor,
403 y / webkit_scale_factor);
404 return point;
405 }
406
407 // Given a text, the positions, and the paint object, this method gets the
408 // coordinates and prints the text at those coordinates on the canvas.
409 void PrintHeaderFooterText(
410 const string16& text,
411 WebKit::WebCanvas* canvas,
412 HeaderFooterPaint paint,
413 float webkit_scale_factor,
414 const printing::PageSizeMargins& page_layout,
415 printing::HorizontalHeaderFooterPosition horizontal_position,
416 printing::VerticalHeaderFooterPosition vertical_position,
417 double offset_to_baseline) {
418 #if defined(USE_RENDER_TEXT)
419 paint->SetText(text);
420 paint->SetFontSize(printing::kSettingHeaderFooterFontSize);
421 double text_width_in_points = paint->GetStringSize().width();
422 SkPoint point = GetHeaderFooterPosition(webkit_scale_factor, page_layout,
423 horizontal_position,
424 vertical_position, offset_to_baseline,
425 text_width_in_points);
426 // Set the scaled font size before drawing the text.
427 // This creates a new font instead of calling |paint->SetFontSize()| to work
428 // around a Windows 8 bug. See: http://crbug.com/139206
429 gfx::FontList font_list(
430 gfx::Font(printing::kSettingHeaderFooterFontFamilyName,
431 printing::kSettingHeaderFooterFontSize / webkit_scale_factor));
432 paint->SetFontList(font_list);
433 gfx::Size size(paint->GetStringSize());
434 gfx::Rect rect(point.x(), point.y() - paint->GetBaseline(),
435 size.width(), size.height());
436 paint->SetDisplayRect(rect);
437 {
438 SkMatrix m = canvas->getTotalMatrix();
439 ui::ScaleFactor device_scale_factor = ui::GetScaleFactorFromScale(
440 SkScalarAbs(m.getScaleX()));
441 scoped_ptr<gfx::Canvas> gfx_canvas(gfx::Canvas::CreateCanvasWithoutScaling(
442 canvas, device_scale_factor));
443 paint->Draw(gfx_canvas.get());
444 }
445 #else
446 // TODO(arthurhsu): following code has issues with i18n BiDi, see
447 // crbug.com/108599.
448 size_t text_byte_length = text.length() * sizeof(char16);
449 double text_width_in_points = SkScalarToDouble(paint.measureText(
450 text.c_str(), text_byte_length));
451 SkPoint point = GetHeaderFooterPosition(webkit_scale_factor, page_layout,
452 horizontal_position,
453 vertical_position, offset_to_baseline,
454 text_width_in_points);
455 paint.setTextSize(SkDoubleToScalar(
456 paint.getTextSize() / webkit_scale_factor));
457 canvas->drawText(text.c_str(), text_byte_length, point.x(), point.y(),
458 paint);
459 #endif
460 }
461
462 PrintMsg_Print_Params CalculatePrintParamsForCss( 346 PrintMsg_Print_Params CalculatePrintParamsForCss(
463 WebKit::WebFrame* frame, 347 WebKit::WebFrame* frame,
464 int page_index, 348 int page_index,
465 const PrintMsg_Print_Params& page_params, 349 const PrintMsg_Print_Params& page_params,
466 bool ignore_css_margins, 350 bool ignore_css_margins,
467 bool fit_to_page, 351 bool fit_to_page,
468 double* scale_factor) { 352 double* scale_factor) {
469 PrintMsg_Print_Params css_params = GetCssPrintParams(frame, page_index, 353 PrintMsg_Print_Params css_params = GetCssPrintParams(frame, page_index,
470 page_params); 354 page_params);
471 355
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
507 switches::kRendererPrintPreview); 391 switches::kRendererPrintPreview);
508 } 392 }
509 393
510 bool IsPrintThrottlingDisabled() { 394 bool IsPrintThrottlingDisabled() {
511 return CommandLine::ForCurrentProcess()->HasSwitch( 395 return CommandLine::ForCurrentProcess()->HasSwitch(
512 switches::kDisableScriptedPrintThrottling); 396 switches::kDisableScriptedPrintThrottling);
513 } 397 }
514 398
515 } // namespace 399 } // namespace
516 400
517 // static - Not anonymous so that platform implementations can use it.
518 void PrintWebViewHelper::PrintHeaderAndFooter( 401 void PrintWebViewHelper::PrintHeaderAndFooter(
vandebo (ex-Chrome) 2012/11/06 23:01:32 nit: Should leave the // static comment.
Vitaly Buka (NO REVIEWS) 2012/11/06 23:16:02 Done.
519 WebKit::WebCanvas* canvas, 402 WebKit::WebCanvas* canvas,
520 int page_number, 403 int page_number,
521 int total_pages, 404 int total_pages,
522 float webkit_scale_factor, 405 float webkit_scale_factor,
523 const printing::PageSizeMargins& page_layout, 406 const printing::PageSizeMargins& page_layout,
524 const DictionaryValue& header_footer_info, 407 const DictionaryValue& header_footer_info,
525 const PrintMsg_Print_Params& params) { 408 const PrintMsg_Print_Params& params) {
526 skia::VectorPlatformDeviceSkia* device = 409 int dpi = GetDPI(&params);
527 static_cast<skia::VectorPlatformDeviceSkia*>(canvas->getTopDevice()); 410 using printing::ConvertUnit;
528 device->setDrawingArea(SkPDFDevice::kMargin_DrawingArea); 411 WebKit::WebSize page_size(
412 ConvertUnit(params.page_size.width(), dpi, params.desired_dpi),
413 ConvertUnit(params.page_size.height(), dpi, params.desired_dpi));
529 414
530 #if defined(USE_RENDER_TEXT) 415 WebKit::WebView* web_view = WebKit::WebView::create(NULL);
531 scoped_ptr<gfx::RenderText> render_text(gfx::RenderText::CreateInstance()); 416 web_view->settings()->setJavaScriptEnabled(true);
532 // TODO(asvitkine): The below line is to workaround http://crbug.com/133548. 417 web_view->initializeMainFrame(NULL);
533 // Remove it when the underlying Skia bug has been fixed.
534 render_text->set_clip_to_display_rect(false);
535 gfx::FontList font_list(
536 gfx::Font(printing::kSettingHeaderFooterFontFamilyName,
537 printing::kSettingHeaderFooterFontSize));
538 gfx::RenderText* paint = render_text.get();
539 #else
540 SkPaint paint;
541 paint.setColor(SK_ColorBLACK);
542 paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
543 paint.setTextSize(SkDoubleToScalar(printing::kSettingHeaderFooterFontSize));
544 paint.setTypeface(SkTypeface::CreateFromName(
545 printing::kSettingHeaderFooterFontFamilyName, SkTypeface::kNormal));
546 #endif
547 418
548 // Print the headers onto the |canvas| if there is enough space to print 419 WebKit::WebFrame* frame = web_view->mainFrame();
549 // them. 420
550 string16 date; 421 base::StringValue html(
551 string16 title; 422 ResourceBundle::GetSharedInstance().GetLocalizedString(
552 if (!header_footer_info.GetString(printing::kSettingHeaderFooterTitle, 423 IDR_PRINT_PREVIEW_PAGE));
553 &title) || 424 // Load page with script to avoid async operations.
554 !header_footer_info.GetString(printing::kSettingHeaderFooterDate, 425 ExecuteScript(frame, kPageLoadScriptFormat, html);
555 &date)) { 426
556 NOTREACHED(); 427 scoped_ptr<base::DictionaryValue> options(header_footer_info.DeepCopy());
428 options->SetDouble("width", page_size.width / webkit_scale_factor);
429 options->SetDouble("height", page_size.height / webkit_scale_factor);
430 options->SetDouble("topMargin", page_layout.margin_top / webkit_scale_factor);
431 options->SetDouble("bottomMargin",
432 page_layout.margin_bottom / webkit_scale_factor);
433 options->SetDouble(
434 "fontSize", printing::kSettingHeaderFooterFontSize / webkit_scale_factor);
435 options->SetString("pageNumber",
436 StringPrintf("%d/%d", page_number, total_pages));
437
438 ExecuteScript(frame, kPageSetupScriptFormat, *options);
439
440 SkAutoCanvasRestore auto_restore(canvas, true);
441 WebKit::WebPrintParams webkit_params(page_size);
442 webkit_params.printerDPI = dpi;
443
444 frame->printBegin(webkit_params, WebKit::WebNode(), NULL);
445 frame->printPage(0, canvas);
446 frame->printEnd();
447
448 if (web_view)
449 web_view->close();
450 }
451
452 float PrintWebViewHelper::RenderPageContent(WebKit::WebFrame* frame,
453 int page_number,
454 const gfx::Rect& canvas_area,
455 const gfx::Rect& content_area,
456 double scale_factor,
457 WebKit::WebCanvas* canvas) {
458 SkAutoCanvasRestore auto_restore(canvas, true);
459 if (content_area != canvas_area) {
460 canvas->translate((content_area.x() - canvas_area.x()) / scale_factor,
461 (content_area.y() - canvas_area.y()) / scale_factor);
462 SkRect clip_rect(
463 SkRect::MakeXYWH(content_area.origin().x() / scale_factor,
464 content_area.origin().y() / scale_factor,
465 content_area.size().width() / scale_factor,
466 content_area.size().height() / scale_factor));
467 SkIRect clip_int_rect;
468 clip_rect.roundOut(&clip_int_rect);
469 SkRegion clip_region(clip_int_rect);
470 canvas->setClipRegion(clip_region);
557 } 471 }
558 string16 header_text = date + title; 472 return frame->printPage(page_number, canvas);
559
560 // Used for height calculations. Note that the width may be undefined.
561 SkRect header_vertical_bounds;
562 #if defined(USE_RENDER_TEXT)
563 paint->SetFontList(font_list);
564 paint->SetText(header_text);
565 {
566 gfx::Rect rect(gfx::Point(), paint->GetStringSize());
567 header_vertical_bounds = gfx::RectToSkRect(rect);
568 header_vertical_bounds.offset(0, -render_text->GetBaseline());
569 }
570 #else
571 paint.measureText(header_text.c_str(), header_text.length() * sizeof(char16),
572 &header_vertical_bounds, 0);
573 #endif
574
575 double text_height = printing::kSettingHeaderFooterInterstice +
576 header_vertical_bounds.height();
577 if (text_height <= page_layout.margin_top) {
578 PrintHeaderFooterText(date, canvas, paint, webkit_scale_factor, page_layout,
579 printing::LEFT, printing::TOP,
580 header_vertical_bounds.top());
581 PrintHeaderFooterText(title, canvas, paint, webkit_scale_factor,
582 page_layout, printing::CENTER, printing::TOP,
583 header_vertical_bounds.top());
584 }
585
586 // Prints the footers onto the |canvas| if there is enough space to print
587 // them.
588 string16 page_of_total_pages = base::IntToString16(page_number) +
589 UTF8ToUTF16("/") +
590 base::IntToString16(total_pages);
591 string16 url;
592 if (!header_footer_info.GetString(printing::kSettingHeaderFooterURL, &url)) {
593 NOTREACHED();
594 }
595 string16 footer_text = page_of_total_pages + url;
596
597 // Used for height calculations. Note that the width may be undefined.
598 SkRect footer_vertical_bounds;
599 #if defined(USE_RENDER_TEXT)
600 paint->SetFontList(font_list);
601 paint->SetText(footer_text);
602 {
603 gfx::Rect rect(gfx::Point(), paint->GetStringSize());
604 footer_vertical_bounds = gfx::RectToSkRect(rect);
605 footer_vertical_bounds.offset(0, -paint->GetBaseline());
606 }
607 #else
608 paint.measureText(footer_text.c_str(), footer_text.length() * sizeof(char16),
609 &footer_vertical_bounds, 0);
610 #endif
611
612 text_height = printing::kSettingHeaderFooterInterstice +
613 footer_vertical_bounds.height();
614 if (text_height <= page_layout.margin_bottom) {
615 PrintHeaderFooterText(page_of_total_pages, canvas, paint,
616 webkit_scale_factor, page_layout, printing::RIGHT,
617 printing::BOTTOM, footer_vertical_bounds.bottom());
618 PrintHeaderFooterText(url, canvas, paint, webkit_scale_factor, page_layout,
619 printing::LEFT, printing::BOTTOM,
620 footer_vertical_bounds.bottom());
621 }
622
623 device->setDrawingArea(SkPDFDevice::kContent_DrawingArea);
624 } 473 }
625 474
626 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint( 475 PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint(
627 const PrintMsg_Print_Params& print_params, 476 const PrintMsg_Print_Params& print_params,
628 WebKit::WebFrame* frame, 477 WebKit::WebFrame* frame,
629 const WebKit::WebNode& node) 478 const WebKit::WebNode& node)
630 : frame_(frame), 479 : frame_(frame),
631 node_to_print_(node), 480 node_to_print_(node),
632 web_view_(frame->view()), 481 web_view_(frame->view()),
633 expected_pages_count_(0), 482 expected_pages_count_(0),
(...skipping 1252 matching lines...) Expand 10 before | Expand all | Expand 10 after
1886 DCHECK(IsRendering()); 1735 DCHECK(IsRendering());
1887 return prep_frame_view_->GetPrintCanvasSize(); 1736 return prep_frame_view_->GetPrintCanvasSize();
1888 } 1737 }
1889 1738
1890 void PrintWebViewHelper::PrintPreviewContext::ClearContext() { 1739 void PrintWebViewHelper::PrintPreviewContext::ClearContext() {
1891 prep_frame_view_.reset(); 1740 prep_frame_view_.reset();
1892 metafile_.reset(); 1741 metafile_.reset();
1893 pages_to_render_.clear(); 1742 pages_to_render_.clear();
1894 error_ = PREVIEW_ERROR_NONE; 1743 error_ = PREVIEW_ERROR_NONE;
1895 } 1744 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698