print_web_view_helper_pdf_win.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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.h"
13#include "printing/metafile_impl.h"
14#include "printing/metafile_skia_wrapper.h"
15#include "printing/page_size_margins.h"
16#include "printing/units.h"
17#include "skia/ext/platform_device.h"
18#include "skia/ext/vector_canvas.h"
19#include "third_party/WebKit/public/web/WebLocalFrame.h"
20
21
22namespace printing {
23
24using blink::WebFrame;
25
26bool PrintWebViewHelper::RenderPreviewPage(
27    int page_number,
28    const PrintMsg_Print_Params& print_params) {
29  PrintMsg_PrintPage_Params page_params;
30  page_params.params = print_params;
31  page_params.page_number = page_number;
32  scoped_ptr<Metafile> draft_metafile;
33  Metafile* initial_render_metafile = print_preview_context_.metafile();
34  if (print_preview_context_.IsModifiable() && is_print_ready_metafile_sent_) {
35    draft_metafile.reset(new PreviewMetafile);
36    initial_render_metafile = draft_metafile.get();
37  }
38
39  base::TimeTicks begin_time = base::TimeTicks::Now();
40  double actual_shrink =
41      static_cast<float>(print_params.desired_dpi / print_params.dpi);
42  PrintPageInternal(page_params,
43                    print_preview_context_.GetPrintCanvasSize(),
44                    print_preview_context_.prepared_frame(),
45                    initial_render_metafile,
46                    true,
47                    &actual_shrink,
48                    NULL,
49                    NULL);
50  print_preview_context_.RenderedPreviewPage(
51      base::TimeTicks::Now() - begin_time);
52  if (draft_metafile.get()) {
53    draft_metafile->FinishDocument();
54  } else if (print_preview_context_.IsModifiable() &&
55             print_preview_context_.generate_draft_pages()) {
56    DCHECK(!draft_metafile.get());
57    draft_metafile.reset(
58        print_preview_context_.metafile()->GetMetafileForCurrentPage());
59  }
60  return PreviewPageRendered(page_number, draft_metafile.get());
61}
62
63bool PrintWebViewHelper::PrintPagesNative(blink::WebFrame* frame,
64                                          int page_count,
65                                          const gfx::Size& canvas_size) {
66  NativeMetafile metafile;
67  if (!metafile.Init())
68    return false;
69
70  const PrintMsg_PrintPages_Params& params = *print_pages_params_;
71  std::vector<int> printed_pages;
72  std::vector<double> shrink;
73  std::vector<gfx::Size> page_size_in_dpi;
74  std::vector<gfx::Rect> content_area_in_dpi;
75  double dpi_shrink =
76      static_cast<float>(params.params.desired_dpi / params.params.dpi);
77
78  if (params.pages.empty()) {
79    for (int i = 0; i < page_count; ++i) {
80      printed_pages.push_back(i);
81    }
82  } else {
83    // TODO(vitalybuka): redesign to make more code cross platform.
84    for (size_t i = 0; i < params.pages.size(); ++i) {
85      if (params.pages[i] >= 0 && params.pages[i] < page_count) {
86        printed_pages.push_back(params.pages[i]);
87      }
88    }
89  }
90  if (printed_pages.empty())
91    return false;
92
93  for (size_t i = 0; i < printed_pages.size(); ++i) {
94    shrink.push_back(dpi_shrink);
95    page_size_in_dpi.push_back(gfx::Size());
96    content_area_in_dpi.push_back(gfx::Rect());
97  }
98
99  PrintMsg_PrintPage_Params page_params;
100  page_params.params = params.params;
101  for (size_t i = 0; i < printed_pages.size(); ++i) {
102    page_params.page_number = printed_pages[i];
103    PrintPageInternal(page_params,
104                      canvas_size,
105                      frame,
106                      &metafile,
107                      false,
108                      &shrink[i],
109                      &page_size_in_dpi[i],
110                      &content_area_in_dpi[i]);
111  }
112
113  // blink::printEnd() for PDF should be called before metafile is closed.
114  FinishFramePrinting();
115
116  metafile.FinishDocument();
117
118  // Get the size of the resulting metafile.
119  uint32 buf_size = metafile.GetDataSize();
120  DCHECK_GT(buf_size, 0u);
121
122  PrintHostMsg_DidPrintPage_Params printed_page_params;
123  printed_page_params.data_size = 0;
124  printed_page_params.document_cookie = params.params.document_cookie;
125  printed_page_params.page_size = params.params.page_size;
126  printed_page_params.content_area = params.params.printable_area;
127
128  {
129    base::SharedMemory shared_buf;
130    // Allocate a shared memory buffer to hold the generated metafile data.
131    if (!shared_buf.CreateAndMapAnonymous(buf_size)) {
132      NOTREACHED() << "Buffer allocation failed";
133      return false;
134    }
135
136    // Copy the bits into shared memory.
137    if (!metafile.GetData(shared_buf.memory(), buf_size)) {
138      NOTREACHED() << "GetData() failed";
139      shared_buf.Unmap();
140      return false;
141    }
142    shared_buf.GiveToProcess(base::GetCurrentProcessHandle(),
143                             &printed_page_params.metafile_data_handle);
144    shared_buf.Unmap();
145
146    printed_page_params.data_size = buf_size;
147    Send(new PrintHostMsg_DuplicateSection(
148        routing_id(),
149        printed_page_params.metafile_data_handle,
150        &printed_page_params.metafile_data_handle));
151  }
152
153  for (size_t i = 0; i < printed_pages.size(); ++i) {
154    printed_page_params.page_number = printed_pages[i];
155    printed_page_params.actual_shrink = shrink[i];
156    printed_page_params.page_size = page_size_in_dpi[i];
157    printed_page_params.content_area = content_area_in_dpi[i];
158    Send(new PrintHostMsg_DidPrintPage(routing_id(), printed_page_params));
159    printed_page_params.metafile_data_handle = INVALID_HANDLE_VALUE;
160  }
161  return true;
162}
163
164void PrintWebViewHelper::PrintPageInternal(
165    const PrintMsg_PrintPage_Params& params,
166    const gfx::Size& canvas_size,
167    WebFrame* frame,
168    Metafile* metafile,
169    bool is_preview,
170    double* actual_shrink,
171    gfx::Size* page_size_in_dpi,
172    gfx::Rect* content_area_in_dpi) {
173  PageSizeMargins page_layout_in_points;
174  double css_scale_factor = 1.0f;
175  ComputePageLayoutInPointsForCss(frame, params.page_number, params.params,
176                                  ignore_css_margins_, &css_scale_factor,
177                                  &page_layout_in_points);
178  gfx::Size page_size;
179  gfx::Rect content_area;
180  GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, &page_size,
181                                          &content_area);
182  int dpi = static_cast<int>(params.params.dpi);
183  // Calculate the actual page size and content area in dpi.
184  if (page_size_in_dpi) {
185    *page_size_in_dpi =
186        gfx::Size(static_cast<int>(ConvertUnitDouble(
187                      page_size.width(), kPointsPerInch, dpi)),
188                  static_cast<int>(ConvertUnitDouble(
189                      page_size.height(), kPointsPerInch, dpi)));
190  }
191
192  if (content_area_in_dpi) {
193    *content_area_in_dpi =
194        gfx::Rect(static_cast<int>(
195                      ConvertUnitDouble(content_area.x(), kPointsPerInch, dpi)),
196                  static_cast<int>(
197                      ConvertUnitDouble(content_area.y(), kPointsPerInch, dpi)),
198                  static_cast<int>(ConvertUnitDouble(
199                      content_area.width(), kPointsPerInch, dpi)),
200                  static_cast<int>(ConvertUnitDouble(
201                      content_area.height(), kPointsPerInch, dpi)));
202  }
203
204  if (!is_preview) {
205    page_size =
206        gfx::Size(static_cast<int>(page_layout_in_points.content_width *
207                                   params.params.max_shrink),
208                  static_cast<int>(page_layout_in_points.content_height *
209                                   params.params.max_shrink));
210  }
211
212  gfx::Rect canvas_area =
213      params.params.display_header_footer ? gfx::Rect(page_size) : content_area;
214
215  float webkit_page_shrink_factor =
216      frame->getPrintPageShrink(params.page_number);
217  float scale_factor = css_scale_factor * webkit_page_shrink_factor;
218
219  SkBaseDevice* device = metafile->StartPageForVectorCanvas(page_size,
220                                                            canvas_area,
221                                                            scale_factor);
222  if (!device)
223    return;
224
225  // The printPage method take a reference to the canvas we pass down, so it
226  // can't be a stack object.
227  skia::RefPtr<skia::VectorCanvas> canvas =
228      skia::AdoptRef(new skia::VectorCanvas(device));
229  MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile);
230  skia::SetIsDraftMode(*canvas, is_print_ready_metafile_sent_);
231
232  if (params.params.display_header_footer) {
233    // |page_number| is 0-based, so 1 is added.
234    PrintHeaderAndFooter(canvas.get(), params.page_number + 1,
235      print_preview_context_.total_page_count(),
236      scale_factor,
237      page_layout_in_points, *header_footer_info_,
238      params.params);
239  }
240
241  float webkit_scale_factor = RenderPageContent(frame,
242                                                params.page_number,
243                                                canvas_area,
244                                                content_area,
245                                                scale_factor,
246                                                canvas.get());
247
248  if (*actual_shrink <= 0 || webkit_scale_factor <= 0) {
249    NOTREACHED() << "Printing page " << params.page_number << " failed.";
250  } else {
251    // While rendering certain plugins (PDF) to metafile, we might need to
252    // set custom scale factor. Update |actual_shrink| with custom scale
253    // if it is set on canvas.
254    // TODO(gene): We should revisit this solution for the next versions.
255    // Consider creating metafile of the right size (or resizable)
256    // https://code.google.com/p/chromium/issues/detail?id=126037
257    if (!MetafileSkiaWrapper::GetCustomScaleOnCanvas(
258            *canvas, actual_shrink)) {
259      // Update the dpi adjustment with the "page |actual_shrink|" calculated in
260      // webkit.
261      *actual_shrink /= (webkit_scale_factor * css_scale_factor);
262    }
263  }
264
265  // Done printing. Close the device context to retrieve the compiled metafile.
266  if (!metafile->FinishPage())
267    NOTREACHED() << "metafile failed";
268}
269
270bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
271    Metafile* metafile, base::SharedMemoryHandle* shared_mem_handle) {
272  uint32 buf_size = metafile->GetDataSize();
273  base::SharedMemory shared_buf;
274  // Allocate a shared memory buffer to hold the generated metafile data.
275  if (!shared_buf.CreateAndMapAnonymous(buf_size)) {
276    NOTREACHED() << "Buffer allocation failed";
277    return false;
278  }
279
280  // Copy the bits into shared memory.
281  if (!metafile->GetData(shared_buf.memory(), buf_size)) {
282    NOTREACHED() << "GetData() failed";
283    shared_buf.Unmap();
284    return false;
285  }
286  shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), shared_mem_handle);
287  shared_buf.Unmap();
288
289  Send(new PrintHostMsg_DuplicateSection(routing_id(), *shared_mem_handle,
290                                         shared_mem_handle));
291  return true;
292}
293
294}  // namespace printing
295