 Chromium Code Reviews
 Chromium Code Reviews Issue 7348010:
  Added Header and Footer support using Skia  (Closed) 
  Base URL: http://git.chromium.org/git/chromium.git@trunk
    
  
    Issue 7348010:
  Added Header and Footer support using Skia  (Closed) 
  Base URL: http://git.chromium.org/git/chromium.git@trunk| Index: chrome/renderer/print_web_view_helper.cc | 
| diff --git a/chrome/renderer/print_web_view_helper.cc b/chrome/renderer/print_web_view_helper.cc | 
| index f55fc3b100bcb0c9aa9b4ab9c894bcaba0761b24..1eb2176bb34a72115ae7374ac81f2258ab7c8de8 100644 | 
| --- a/chrome/renderer/print_web_view_helper.cc | 
| +++ b/chrome/renderer/print_web_view_helper.cc | 
| @@ -4,12 +4,17 @@ | 
| #include "chrome/renderer/print_web_view_helper.h" | 
| +#include <algorithm> | 
| +#include <cmath> | 
| #include <string> | 
| #include "base/command_line.h" | 
| +#include "base/i18n/time_formatting.h" | 
| #include "base/logging.h" | 
| #include "base/metrics/histogram.h" | 
| #include "base/process_util.h" | 
| +#include "base/string_number_conversions.h" | 
| +#include "base/time.h" | 
| #include "base/utf_string_conversions.h" | 
| #include "chrome/common/chrome_switches.h" | 
| #include "chrome/common/print_messages.h" | 
| @@ -21,6 +26,8 @@ | 
| #include "printing/metafile_impl.h" | 
| #include "printing/print_job_constants.h" | 
| #include "printing/units.h" | 
| +#include "skia/ext/vector_platform_device_skia.h" | 
| +#include "third_party/skia/include/core/SkTypeface.h" | 
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebConsoleMessage.h" | 
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebDataSource.h" | 
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | 
| @@ -32,13 +39,16 @@ | 
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h" | 
| #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | 
| #include "ui/base/l10n/l10n_util.h" | 
| +#include "ui/base/text/text_elider.h" | 
| #if defined(OS_POSIX) | 
| #include "content/common/view_messages.h" | 
| #endif | 
| +using base::Time; | 
| using printing::ConvertPixelsToPoint; | 
| using printing::ConvertPixelsToPointDouble; | 
| +using printing::ConvertPointsToPixelDouble; | 
| using printing::ConvertUnit; | 
| using printing::ConvertUnitDouble; | 
| using WebKit::WebConsoleMessage; | 
| @@ -88,12 +98,217 @@ bool PrintMsg_Print_Params_IsEqual( | 
| oldParams.params.supports_alpha_blend == | 
| newParams.params.supports_alpha_blend && | 
| oldParams.pages.size() == newParams.pages.size() && | 
| + oldParams.params.display_header_footer == | 
| + newParams.params.display_header_footer && | 
| std::equal(oldParams.pages.begin(), oldParams.pages.end(), | 
| newParams.pages.begin()); | 
| } | 
| +// Gets the byte length of the |text|. | 
| +size_t GetString16ByteLength(string16 text) { | 
| 
vandebo (ex-Chrome)
2011/07/22 22:58:33
I don't think this needs to be a function, go ahea
 
Aayush Kumar
2011/07/24 02:09:02
Done.
 | 
| + return text.length() * sizeof(char16); | 
| +} | 
| + | 
| +// Splits the horizontal width equally into three segments with an interstice | 
| +// between each segment. Returns the width of this segment. | 
| +SkScalar GetSegmentWidth(PageSizeMargins& page_size_margins) { | 
| + return (page_size_margins.margin_left + | 
| + page_size_margins.content_width + | 
| + page_size_margins.margin_right - | 
| + (printing::kSettingHeaderFooterHorizontalRegions + 1) * | 
| + printing::kSettingHeaderFooterInterstice) / | 
| + printing::kSettingHeaderFooterHorizontalRegions; | 
| +} | 
| + | 
| +// Given a SkPaint object, gets the maximum possible height of any glyph. | 
| +SkScalar GetMaxTextHeight(SkPaint paint) { | 
| + SkPaint::FontMetrics metrics; | 
| + paint.getFontMetrics(&metrics); | 
| + return metrics.fBottom - metrics.fTop; | 
| +} | 
| + | 
| +// Gets the (x, y) coordinate from where we want to start printing the current | 
| +// text depending on the alignment horizontal alignment (LEFT, RIGHT, CENTER) | 
| +// and vertical alignment (TOP, BOTTOM). | 
| +SkPoint GetHeaderFooterPosition( | 
| + string16 text, | 
| + SkPaint paint, | 
| + printing::HorizontalHeaderFooterPosition hor_pos, | 
| + printing::VerticalHeaderFooterPosition ver_pos, | 
| + PageSizeMargins& page_size_margins) { | 
| + | 
| + SkScalar text_width_in_points = paint.measureText( | 
| + text.c_str(), GetString16ByteLength(text)); | 
| + | 
| + SkScalar x = 0; | 
| + switch (hor_pos) { | 
| + case printing::LEFT: { | 
| + x = page_size_margins.margin_left * (-1) + | 
| + printing::kSettingHeaderFooterInterstice; | 
| + break; | 
| + } | 
| + case printing::RIGHT: { | 
| + x = ((page_size_margins.content_width + page_size_margins.margin_right) - | 
| + (printing::kSettingHeaderFooterInterstice + text_width_in_points)); | 
| + break; | 
| + } | 
| + case printing::CENTER: { | 
| + SkScalar available_width = GetSegmentWidth(page_size_margins); | 
| + x = (available_width - page_size_margins.margin_left + | 
| + (available_width - text_width_in_points) / 2); | 
| + break; | 
| + } | 
| + default: { | 
| + NOTREACHED(); | 
| + } | 
| + } | 
| + | 
| + SkScalar y = 0; | 
| + SkPaint::FontMetrics metrics; | 
| + paint.getFontMetrics(&metrics); | 
| + switch (ver_pos) { | 
| + case printing::TOP: { | 
| + y = page_size_margins.margin_top * (-1) + | 
| + printing::kSettingHeaderFooterInterstice - metrics.fTop; | 
| + break; | 
| + } | 
| + case printing::BOTTOM: { | 
| + y = page_size_margins.margin_bottom + page_size_margins.content_height - | 
| + printing::kSettingHeaderFooterInterstice - metrics.fBottom; | 
| + break; | 
| + } | 
| + default: { | 
| + NOTREACHED(); | 
| + } | 
| + } | 
| + | 
| + SkPoint point; | 
| + point.set(x, y); | 
| + return point; | 
| +} | 
| + | 
| +// Given a text, the positions and the paint object, this method gets the | 
| +// coordinates and prints the text at those co-ordinates on the canvas. | 
| +void PrintHeaderFooterText( | 
| + string16 text, | 
| + SkPaint paint, | 
| + const SkRefPtr<skia::VectorCanvas>& canvas, | 
| + printing::HorizontalHeaderFooterPosition hor_pos, | 
| + printing::VerticalHeaderFooterPosition ver_pos, | 
| + float webkit_scale_factor, | 
| + PageSizeMargins& page_size_margins) { | 
| + SkPoint point = GetHeaderFooterPosition(text, paint, hor_pos, ver_pos, | 
| 
vandebo (ex-Chrome)
2011/07/22 22:58:33
Maybe just inline getheaderfooterposition here, si
 
Aayush Kumar
2011/07/24 02:09:02
Done.
 | 
| + page_size_margins); | 
| + point.set(point.x() / webkit_scale_factor, point.y() / webkit_scale_factor); | 
| + size_t byte_length = GetString16ByteLength(text); | 
| + | 
| + canvas->drawText(text.c_str(), byte_length, point.x(), point.y(), paint); | 
| +} | 
| + | 
| } // namespace | 
| +void PrintHeaderAndFooter(skia::VectorPlatformDeviceSkia* device, | 
| + const SkRefPtr<skia::VectorCanvas>& canvas, | 
| + int page_number, int total_pages, | 
| + float webkit_scale_factor, | 
| + PageSizeMargins& page_size_margins, | 
| + const DictionaryValue* header_footer_info) { | 
| + // Set the drawing area to draw in the margins. | 
| + device->setDrawingArea(SkPDFDevice::kMargin_DrawingArea); | 
| + | 
| + // Setting up styles for the headers and footers text. | 
| + SkPaint paint; | 
| + paint.setColor(SK_ColorBLACK); | 
| + paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); | 
| + | 
| + SkScalar font_size = printing::kSettingHeaderFooterFontSize / | 
| + webkit_scale_factor; | 
| + paint.setTextSize(font_size); | 
| + gfx::Font font(UTF8ToUTF16(printing::kSettingHeaderFooterFontName), | 
| + ceil(ConvertPointsToPixelDouble(font_size))); | 
| + paint.setTypeface(SkTypeface::CreateFromName( | 
| + UTF16ToUTF8(font.GetFontName()).c_str(), | 
| + SkTypeface::kNormal)); | 
| + | 
| + // Ensuring we have enough space to print above and below the page to print | 
| + // headers and footers. | 
| + SkScalar text_height = printing::kSettingHeaderFooterInterstice + | 
| + GetMaxTextHeight(paint); | 
| 
vandebo (ex-Chrome)
2011/07/22 22:58:33
Why not measure the actual text height here, inste
 
Aayush Kumar
2011/07/24 02:09:02
Done.
 | 
| + if (text_height > page_size_margins.margin_top || | 
| + text_height > page_size_margins.margin_bottom) { | 
| + return; | 
| + } | 
| + | 
| + SkScalar segment_width = GetSegmentWidth(page_size_margins); | 
| + // Printing the Date. | 
| + string16 date = base::TimeFormatShortDateNumeric(Time::Now()); | 
| + date = date; | 
| + date = ui::ElideText(date, font, ConvertPointsToPixelDouble(segment_width), | 
| 
vandebo (ex-Chrome)
2011/07/22 22:58:33
Can you pull the ElideText into the PrintHeaderFoo
 
Aayush Kumar
2011/07/24 02:09:02
Done.
 | 
| + false); | 
| + PrintHeaderFooterText(date, paint, canvas, printing::LEFT, printing::TOP, | 
| + webkit_scale_factor, page_size_margins); | 
| + | 
| + // Printing the title. | 
| + string16 title; | 
| + if (!header_footer_info->GetString(printing::kSettingHeaderFooterTitle, | 
| + &title)) { | 
| + NOTREACHED(); | 
| + } | 
| + | 
| + SkScalar date_width = paint.measureText(date.c_str(), | 
| + GetString16ByteLength(date)); | 
| + // Calculating the available title width. If the date string is not long | 
| + // enough, then, we increase the available space we have for the title. | 
| + // Assumes there is no header text to RIGHT of title. | 
| + SkScalar max_title_width = std::min(2 * segment_width, | 
| + 2 * (segment_width - date_width) + | 
| + segment_width); | 
| + title = ui::ElideText(title, font, | 
| + ConvertPointsToPixelDouble(max_title_width), false); | 
| + PrintHeaderFooterText(title, paint, canvas, printing::CENTER, | 
| + printing::TOP, webkit_scale_factor, | 
| + page_size_margins); | 
| + | 
| + // Printing the page numbers at the bottom right corner of page. | 
| + string16 page_on_page_total = base::IntToString16(page_number) + | 
| + UTF8ToUTF16("/") + | 
| + base::IntToString16(total_pages); | 
| + page_on_page_total = ui::ElideText(page_on_page_total, font, | 
| + ConvertPointsToPixelDouble(segment_width), | 
| + false); | 
| + PrintHeaderFooterText(page_on_page_total, paint, canvas, printing::RIGHT, | 
| + printing::BOTTOM, webkit_scale_factor, | 
| + page_size_margins); | 
| + | 
| + // Printing the URL. | 
| + std::string url; | 
| + if (!header_footer_info->GetString(printing::kSettingHeaderFooterURL, | 
| + &url)) { | 
| + NOTREACHED(); | 
| + } | 
| + GURL gurl(url); | 
| + SkScalar page_width = paint.measureText( | 
| + page_on_page_total.c_str(), | 
| + GetString16ByteLength(page_on_page_total)); | 
| + | 
| + // Calculating the available URL width. We increase the available URL width | 
| + // if the |page_on_page_total| string isn't long enough. | 
| + // Assumes no footer text being printed in the CENTER. | 
| + SkScalar max_url_width = 3 * (page_size_margins.content_width + | 
| + page_size_margins.margin_left + page_size_margins.margin_right) / 4; | 
| + max_url_width = std::min(max_url_width, | 
| + 2 * (segment_width - page_width) + segment_width); | 
| + string16 url_elided = ui::ElideUrl(gurl, font, | 
| + ConvertPointsToPixelDouble(max_url_width), | 
| + std::string()); | 
| + PrintHeaderFooterText(url_elided, paint, canvas, printing::LEFT, | 
| + printing::BOTTOM, webkit_scale_factor, | 
| + page_size_margins); | 
| + | 
| + // Restore the drawing area to draw in the content area. | 
| + device->setDrawingArea(SkPDFDevice::kContent_DrawingArea); | 
| +} | 
| + | 
| PrepareFrameAndViewForPrint::PrepareFrameAndViewForPrint( | 
| const PrintMsg_Print_Params& print_params, | 
| WebFrame* frame, | 
| @@ -261,9 +476,12 @@ void PrintWebViewHelper::OnPrintPages() { | 
| Print(frame, NULL); | 
| } | 
| -void PrintWebViewHelper::OnPrintPreview(const DictionaryValue& settings) { | 
| +void PrintWebViewHelper::OnPrintPreview( | 
| + const DictionaryValue& settings, | 
| + const DictionaryValue& header_footer_info) { | 
| DCHECK(is_preview_); | 
| print_preview_context_.OnPrintPreview(); | 
| + header_footer_info_ = header_footer_info.DeepCopy(); | 
| if (!InitPrintSettings(print_preview_context_.frame(), | 
| print_preview_context_.node())) { | 
| @@ -552,16 +770,10 @@ void PrintWebViewHelper::didStopLoading() { | 
| PrintPages(*params, print_web_view_->mainFrame(), NULL); | 
| } | 
| -void PrintWebViewHelper::GetPageSizeAndMarginsInPoints( | 
| +PageSizeMargins PrintWebViewHelper::GetPageSizeAndMarginsInPoints( | 
| WebFrame* frame, | 
| int page_index, | 
| - const PrintMsg_Print_Params& default_params, | 
| - double* content_width_in_points, | 
| - double* content_height_in_points, | 
| - double* margin_top_in_points, | 
| - double* margin_right_in_points, | 
| - double* margin_bottom_in_points, | 
| - double* margin_left_in_points) { | 
| + const PrintMsg_Print_Params& default_params) { | 
| int dpi = GetDPI(&default_params); | 
| WebSize page_size_in_pixels( | 
| @@ -593,67 +805,53 @@ void PrintWebViewHelper::GetPageSizeAndMarginsInPoints( | 
| margin_left_in_pixels); | 
| } | 
| - *content_width_in_points = ConvertPixelsToPoint(page_size_in_pixels.width - | 
| - margin_left_in_pixels - | 
| - margin_right_in_pixels); | 
| - *content_height_in_points = ConvertPixelsToPoint(page_size_in_pixels.height - | 
| - margin_top_in_pixels - | 
| - margin_bottom_in_pixels); | 
| + PageSizeMargins page_size_margins_in_points; | 
| + page_size_margins_in_points.content_width = ConvertPixelsToPoint( | 
| + page_size_in_pixels.width - margin_left_in_pixels - | 
| + margin_right_in_pixels); | 
| + page_size_margins_in_points.content_height = ConvertPixelsToPoint( | 
| + page_size_in_pixels.height - margin_top_in_pixels - | 
| + margin_bottom_in_pixels); | 
| // Invalid page size and/or margins. We just use the default setting. | 
| - if (*content_width_in_points < 1.0 || *content_height_in_points < 1.0) { | 
| - GetPageSizeAndMarginsInPoints(NULL, | 
| - page_index, | 
| - default_params, | 
| - content_width_in_points, | 
| - content_height_in_points, | 
| - margin_top_in_points, | 
| - margin_right_in_points, | 
| - margin_bottom_in_points, | 
| - margin_left_in_points); | 
| - return; | 
| + if (page_size_margins_in_points.content_width < 1.0 || | 
| + page_size_margins_in_points.content_height < 1.0) { | 
| + return GetPageSizeAndMarginsInPoints(NULL, page_index, default_params); | 
| } | 
| - if (margin_top_in_points) | 
| - *margin_top_in_points = | 
| - ConvertPixelsToPointDouble(margin_top_in_pixels); | 
| - if (margin_right_in_points) | 
| - *margin_right_in_points = | 
| - ConvertPixelsToPointDouble(margin_right_in_pixels); | 
| - if (margin_bottom_in_points) | 
| - *margin_bottom_in_points = | 
| - ConvertPixelsToPointDouble(margin_bottom_in_pixels); | 
| - if (margin_left_in_points) | 
| - *margin_left_in_points = | 
| - ConvertPixelsToPointDouble(margin_left_in_pixels); | 
| + page_size_margins_in_points.margin_top = | 
| + ConvertPixelsToPointDouble(margin_top_in_pixels); | 
| + page_size_margins_in_points.margin_right = | 
| + ConvertPixelsToPointDouble(margin_right_in_pixels); | 
| + page_size_margins_in_points.margin_bottom = | 
| + ConvertPixelsToPointDouble(margin_bottom_in_pixels); | 
| + page_size_margins_in_points.margin_left = | 
| + ConvertPixelsToPointDouble(margin_left_in_pixels); | 
| + return page_size_margins_in_points; | 
| } | 
| void PrintWebViewHelper::UpdatePrintableSizeInPrintParameters( | 
| WebFrame* frame, | 
| WebNode* node, | 
| PrintMsg_Print_Params* params) { | 
| - double content_width_in_points; | 
| - double content_height_in_points; | 
| - double margin_top_in_points; | 
| - double margin_right_in_points; | 
| - double margin_bottom_in_points; | 
| - double margin_left_in_points; | 
| PrepareFrameAndViewForPrint prepare(*params, frame, node); | 
| - PrintWebViewHelper::GetPageSizeAndMarginsInPoints(frame, 0, *params, | 
| - &content_width_in_points, &content_height_in_points, | 
| - &margin_top_in_points, &margin_right_in_points, | 
| - &margin_bottom_in_points, &margin_left_in_points); | 
| + PageSizeMargins page_size_margins_in_points = | 
| + PrintWebViewHelper::GetPageSizeAndMarginsInPoints(frame, 0, *params); | 
| int dpi = GetDPI(params); | 
| params->printable_size = gfx::Size( | 
| - static_cast<int>(ConvertUnitDouble(content_width_in_points, | 
| + static_cast<int>(ConvertUnitDouble( | 
| + page_size_margins_in_points.content_width, | 
| printing::kPointsPerInch, dpi)), | 
| - static_cast<int>(ConvertUnitDouble(content_height_in_points, | 
| + static_cast<int>(ConvertUnitDouble( | 
| + page_size_margins_in_points.content_height, | 
| printing::kPointsPerInch, dpi))); | 
| - double page_width_in_points = content_width_in_points + | 
| - margin_left_in_points + margin_right_in_points; | 
| - double page_height_in_points = content_height_in_points + | 
| - margin_top_in_points + margin_bottom_in_points; | 
| + double page_width_in_points = page_size_margins_in_points.content_width + | 
| + page_size_margins_in_points.margin_left + | 
| + page_size_margins_in_points.margin_right; | 
| + double page_height_in_points = page_size_margins_in_points.content_height + | 
| + page_size_margins_in_points.margin_top + | 
| + page_size_margins_in_points.margin_bottom; | 
| params->page_size = gfx::Size( | 
| static_cast<int>(ConvertUnitDouble( | 
| @@ -662,9 +860,9 @@ void PrintWebViewHelper::UpdatePrintableSizeInPrintParameters( | 
| page_height_in_points, printing::kPointsPerInch, dpi))); | 
| params->margin_top = static_cast<int>(ConvertUnitDouble( | 
| - margin_top_in_points, printing::kPointsPerInch, dpi)); | 
| + page_size_margins_in_points.margin_top, printing::kPointsPerInch, dpi)); | 
| params->margin_left = static_cast<int>(ConvertUnitDouble( | 
| - margin_left_in_points, printing::kPointsPerInch, dpi)); | 
| + page_size_margins_in_points.margin_left, printing::kPointsPerInch, dpi)); | 
| } | 
| bool PrintWebViewHelper::InitPrintSettings(WebKit::WebFrame* frame, | 
| @@ -720,6 +918,7 @@ bool PrintWebViewHelper::UpdatePrintSettingsCloud( | 
| settings.params.supports_alpha_blend = false; | 
| // TODO(abodenha@chromium.org) Parse page ranges from the job_settings. | 
| print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings)); | 
| + print_pages_params_->params.display_header_footer = false; | 
| return true; | 
| } | 
| @@ -740,6 +939,11 @@ bool PrintWebViewHelper::UpdatePrintSettingsLocal( | 
| } | 
| print_pages_params_.reset(new PrintMsg_PrintPages_Params(settings)); | 
| + bool header_footer; | 
| + if (!job_settings.GetBoolean(printing::kSettingHeaderFooter, &header_footer)) | 
| + NOTREACHED(); | 
| + print_pages_params_->params.display_header_footer = header_footer; | 
| + | 
| Send(new PrintHostMsg_DidGetDocumentCookie(routing_id(), | 
| settings.params.document_cookie)); | 
| return true; |