1// Copyright 2014 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 "chrome/renderer/printing/print_web_view_helper.h"
6
7#include "base/logging.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/process/process_handle.h"
10#include "chrome/common/print_messages.h"
11#include "content/public/renderer/render_thread.h"
12#include "printing/metafile_skia_wrapper.h"
13#include "printing/page_size_margins.h"
14#include "printing/pdf_metafile_skia.h"
15#include "printing/units.h"
16#include "skia/ext/platform_device.h"
17#include "skia/ext/vector_canvas.h"
18#include "third_party/WebKit/public/web/WebLocalFrame.h"
19
20
21namespace printing {
22
23using blink::WebFrame;
24
25bool PrintWebViewHelper::RenderPreviewPage(
26    int page_number,
27    const PrintMsg_Print_Params& print_params) {
28  PrintMsg_PrintPage_Params page_params;
29  page_params.params = print_params;
30  page_params.page_number = page_number;
31  scoped_ptr<PdfMetafileSkia> draft_metafile;
32  PdfMetafileSkia* initial_render_metafile = print_preview_context_.metafile();
33  if (print_preview_context_.IsModifiable() && is_print_ready_metafile_sent_) {
34    draft_metafile.reset(new PdfMetafileSkia);
35    initial_render_metafile = draft_metafile.get();
36  }
37
38  base::TimeTicks begin_time = base::TimeTicks::Now();
39  PrintPageInternal(page_params,
40                    print_preview_context_.prepared_frame(),
41                    initial_render_metafile,
42                    NULL,
43                    NULL);
44  print_preview_context_.RenderedPreviewPage(
45      base::TimeTicks::Now() - begin_time);
46  if (draft_metafile.get()) {
47    draft_metafile->FinishDocument();
48  } else if (print_preview_context_.IsModifiable() &&
49             print_preview_context_.generate_draft_pages()) {
50    DCHECK(!draft_metafile.get());
51    draft_metafile =
52        print_preview_context_.metafile()->GetMetafileForCurrentPage();
53  }
54  return PreviewPageRendered(page_number, draft_metafile.get());
55}
56
57bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame,
58                                          int page_count) {
59  PdfMetafileSkia metafile;
60  if (!metafile.Init())
61    return false;
62
63  const PrintMsg_PrintPages_Params& params = *print_pages_params_;
64  std::vector<int> printed_pages;
65  if (params.pages.empty()) {
66    for (int i = 0; i < page_count; ++i) {
67      printed_pages.push_back(i);
68    }
69  } else {
70    // TODO(vitalybuka): redesign to make more code cross platform.
71    for (size_t i = 0; i < params.pages.size(); ++i) {
72      if (params.pages[i] >= 0 && params.pages[i] < page_count) {
73        printed_pages.push_back(params.pages[i]);
74      }
75    }
76  }
77  if (printed_pages.empty())
78    return false;
79
80  std::vector<gfx::Size> page_size_in_dpi(printed_pages.size());
81  std::vector<gfx::Rect> content_area_in_dpi(printed_pages.size());
82
83  PrintMsg_PrintPage_Params page_params;
84  page_params.params = params.params;
85  for (size_t i = 0; i < printed_pages.size(); ++i) {
86    page_params.page_number = printed_pages[i];
87    PrintPageInternal(page_params,
88                      frame,
89                      &metafile,
90                      &page_size_in_dpi[i],
91                      &content_area_in_dpi[i]);
92  }
93
94  // blink::printEnd() for PDF should be called before metafile is closed.
95  FinishFramePrinting();
96
97  metafile.FinishDocument();
98
99  // Get the size of the resulting metafile.
100  uint32 buf_size = metafile.GetDataSize();
101  DCHECK_GT(buf_size, 0u);
102
103  PrintHostMsg_DidPrintPage_Params printed_page_params;
104  printed_page_params.data_size = 0;
105  printed_page_params.document_cookie = params.params.document_cookie;
106  printed_page_params.page_size = params.params.page_size;
107  printed_page_params.content_area = params.params.printable_area;
108
109  {
110    base::SharedMemory shared_buf;
111    // Allocate a shared memory buffer to hold the generated metafile data.
112    if (!shared_buf.CreateAndMapAnonymous(buf_size)) {
113      NOTREACHED() << "Buffer allocation failed";
114      return false;
115    }
116
117    // Copy the bits into shared memory.
118    if (!metafile.GetData(shared_buf.memory(), buf_size)) {
119      NOTREACHED() << "GetData() failed";
120      shared_buf.Unmap();
121      return false;
122    }
123    shared_buf.GiveToProcess(base::GetCurrentProcessHandle(),
124                             &printed_page_params.metafile_data_handle);
125    shared_buf.Unmap();
126
127    printed_page_params.data_size = buf_size;
128    Send(new PrintHostMsg_DuplicateSection(
129        routing_id(),
130        printed_page_params.metafile_data_handle,
131        &printed_page_params.metafile_data_handle));
132  }
133
134  for (size_t i = 0; i < printed_pages.size(); ++i) {
135    printed_page_params.page_number = printed_pages[i];
136    printed_page_params.page_size = page_size_in_dpi[i];
137    printed_page_params.content_area = content_area_in_dpi[i];
138    Send(new PrintHostMsg_DidPrintPage(routing_id(), printed_page_params));
139    printed_page_params.metafile_data_handle = INVALID_HANDLE_VALUE;
140  }
141  return true;
142}
143
144void PrintWebViewHelper::PrintPageInternal(
145    const PrintMsg_PrintPage_Params& params,
146    WebFrame* frame,
147    PdfMetafileSkia* metafile,
148    gfx::Size* page_size_in_dpi,
149    gfx::Rect* content_area_in_dpi) {
150  PageSizeMargins page_layout_in_points;
151  double css_scale_factor = 1.0f;
152  ComputePageLayoutInPointsForCss(frame, params.page_number, params.params,
153                                  ignore_css_margins_, &css_scale_factor,
154                                  &page_layout_in_points);
155  gfx::Size page_size;
156  gfx::Rect content_area;
157  GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, &page_size,
158                                          &content_area);
159  int dpi = static_cast<int>(params.params.dpi);
160  // Calculate the actual page size and content area in dpi.
161  if (page_size_in_dpi) {
162    *page_size_in_dpi =
163        gfx::Size(static_cast<int>(ConvertUnitDouble(
164                      page_size.width(), kPointsPerInch, dpi)),
165                  static_cast<int>(ConvertUnitDouble(
166                      page_size.height(), kPointsPerInch, dpi)));
167  }
168
169  if (content_area_in_dpi) {
170    // Output PDF matches paper size and should be printer edge to edge.
171    *content_area_in_dpi =
172        gfx::Rect(0, 0, page_size_in_dpi->width(), page_size_in_dpi->height());
173  }
174
175  gfx::Rect canvas_area =
176      params.params.display_header_footer ? gfx::Rect(page_size) : content_area;
177
178  float webkit_page_shrink_factor =
179      frame->getPrintPageShrink(params.page_number);
180  float scale_factor = css_scale_factor * webkit_page_shrink_factor;
181
182  SkBaseDevice* device = metafile->StartPageForVectorCanvas(page_size,
183                                                            canvas_area,
184                                                            scale_factor);
185  if (!device)
186    return;
187
188  // The printPage method take a reference to the canvas we pass down, so it
189  // can't be a stack object.
190  skia::RefPtr<skia::VectorCanvas> canvas =
191      skia::AdoptRef(new skia::VectorCanvas(device));
192  MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile);
193  skia::SetIsDraftMode(*canvas, is_print_ready_metafile_sent_);
194
195  if (params.params.display_header_footer) {
196    // |page_number| is 0-based, so 1 is added.
197    PrintHeaderAndFooter(canvas.get(),
198                         params.page_number + 1,
199                         print_preview_context_.total_page_count(),
200                         *frame,
201                         scale_factor,
202                         page_layout_in_points,
203                         params.params);
204  }
205
206  float webkit_scale_factor = RenderPageContent(frame,
207                                                params.page_number,
208                                                canvas_area,
209                                                content_area,
210                                                scale_factor,
211                                                canvas.get());
212  DCHECK_GT(webkit_scale_factor, 0.0f);
213  // Done printing. Close the device context to retrieve the compiled metafile.
214  if (!metafile->FinishPage())
215    NOTREACHED() << "metafile failed";
216}
217
218bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
219    PdfMetafileSkia* metafile,
220    base::SharedMemoryHandle* shared_mem_handle) {
221  uint32 buf_size = metafile->GetDataSize();
222  base::SharedMemory shared_buf;
223  // Allocate a shared memory buffer to hold the generated metafile data.
224  if (!shared_buf.CreateAndMapAnonymous(buf_size)) {
225    NOTREACHED() << "Buffer allocation failed";
226    return false;
227  }
228
229  // Copy the bits into shared memory.
230  if (!metafile->GetData(shared_buf.memory(), buf_size)) {
231    NOTREACHED() << "GetData() failed";
232    shared_buf.Unmap();
233    return false;
234  }
235  shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), shared_mem_handle);
236  shared_buf.Unmap();
237
238  Send(new PrintHostMsg_DuplicateSection(routing_id(), *shared_mem_handle,
239                                         shared_mem_handle));
240  return true;
241}
242
243}  // namespace printing
244