summaryrefslogtreecommitdiffstats
path: root/chrome/renderer/print_web_view_helper_mac.mm
blob: 508fde25f77605b786a4b918d554f0b0fffcb74f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/renderer/print_web_view_helper.h"

#import <AppKit/AppKit.h>

#include "base/logging.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/metrics/histogram.h"
#include "chrome/common/print_messages.h"
#include "printing/metafile.h"
#include "printing/metafile_impl.h"
#include "printing/page_size_margins.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"

#if defined(USE_SKIA)
#include "printing/metafile_skia_wrapper.h"
#include "skia/ext/platform_device.h"
#include "skia/ext/vector_canvas.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCanvas.h"
#endif

using WebKit::WebFrame;

void PrintWebViewHelper::PrintPageInternal(
    const PrintMsg_PrintPage_Params& params,
    const gfx::Size& canvas_size,
    WebFrame* frame) {
  printing::NativeMetafile metafile;
  if (!metafile.Init())
    return;

  int page_number = params.page_number;
  gfx::Size page_size_in_dpi;
  gfx::Rect content_area_in_dpi;
  RenderPage(print_pages_params_->params, page_number, frame, false, &metafile,
             &page_size_in_dpi, &content_area_in_dpi);
  metafile.FinishDocument();

  PrintHostMsg_DidPrintPage_Params page_params;
  page_params.data_size = metafile.GetDataSize();
  page_params.page_number = page_number;
  page_params.document_cookie = params.params.document_cookie;
  page_params.page_size = page_size_in_dpi;
  page_params.content_area = content_area_in_dpi;

  // Ask the browser to create the shared memory for us.
  if (!CopyMetafileDataToSharedMem(&metafile,
                                   &(page_params.metafile_data_handle))) {
    page_params.data_size = 0;
  }

  Send(new PrintHostMsg_DidPrintPage(routing_id(), page_params));
}

bool PrintWebViewHelper::RenderPreviewPage(int page_number) {
  PrintMsg_Print_Params printParams = print_preview_context_.print_params();
  scoped_ptr<printing::Metafile> draft_metafile;
  printing::Metafile* initial_render_metafile =
      print_preview_context_.metafile();

#if defined(USE_SKIA)
  bool render_to_draft = print_preview_context_.IsModifiable() &&
                         is_print_ready_metafile_sent_;
#else
  // If the page needs to be in both draft metafile and print ready metafile,
  // we should always render to the draft metafile first and then copy that
  // into the print ready metafile because CG does not allow us to do it in
  // the other order.
  bool render_to_draft = print_preview_context_.IsModifiable() &&
                         print_preview_context_.generate_draft_pages();
#endif

  if (render_to_draft) {
    draft_metafile.reset(new printing::PreviewMetafile());
    if (!draft_metafile->Init()) {
      print_preview_context_.set_error(
          PREVIEW_ERROR_MAC_DRAFT_METAFILE_INIT_FAILED);
      LOG(ERROR) << "Draft PreviewMetafile Init failed";
      return false;
    }
    initial_render_metafile = draft_metafile.get();
  }

  base::TimeTicks begin_time = base::TimeTicks::Now();
  gfx::Size page_size;
  RenderPage(printParams, page_number, print_preview_context_.frame(), true,
             initial_render_metafile, &page_size, NULL);
  print_preview_context_.RenderedPreviewPage(
      base::TimeTicks::Now() - begin_time);

  if (draft_metafile.get()) {
    draft_metafile->FinishDocument();
#if !defined(USE_SKIA)
    if (!is_print_ready_metafile_sent_) {
      // With CG, we rendered into a new metafile so we could get it as a draft
      // document.  Now we need to add it to print ready document.  But the
      // document has already been scaled and adjusted for margins, so do a 1:1
      // drawing.
      printing::Metafile* print_ready_metafile =
          print_preview_context_.metafile();
      bool success = print_ready_metafile->StartPage(page_size,
                                                     gfx::Rect(page_size), 1.0);
      DCHECK(success);
      // StartPage unconditionally flips the content over, flip it back since it
      // was already flipped in |draft_metafile|.
      CGContextTranslateCTM(print_ready_metafile->context(), 0,
                            page_size.height());
      CGContextScaleCTM(print_ready_metafile->context(), 1.0, -1.0);
      draft_metafile->RenderPage(1,
                                 print_ready_metafile->context(),
                                 draft_metafile->GetPageBounds(1).ToCGRect(),
                                 false /* shrink_to_fit */,
                                 false /* stretch_to_fit */,
                                 true /* center_horizontally */,
                                 true /* center_vertically */);
      print_ready_metafile->FinishPage();
    }
#endif
  } else {
#if defined(USE_SKIA)
    if (print_preview_context_.IsModifiable() &&
        print_preview_context_.generate_draft_pages()) {
      DCHECK(!draft_metafile.get());
      draft_metafile.reset(
          print_preview_context_.metafile()->GetMetafileForCurrentPage());
    }
#endif
  }
  return PreviewPageRendered(page_number, draft_metafile.get());
}

void PrintWebViewHelper::RenderPage(
    const PrintMsg_Print_Params& params, int page_number, WebFrame* frame,
    bool is_preview, printing::Metafile* metafile, gfx::Size* page_size,
    gfx::Rect* content_rect) {
  double scale_factor = 1.0f;
  double webkit_shrink_factor = frame->getPrintPageShrink(page_number);
  printing::PageSizeMargins page_layout_in_points;
  gfx::Rect content_area;

  ComputePageLayoutInPointsForCss(frame, page_number, params,
                                  ignore_css_margins_, fit_to_page_,
                                  &scale_factor, &page_layout_in_points);
  GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, page_size,
                                          &content_area);
  if (content_rect)
    *content_rect = content_area;

  scale_factor *= webkit_shrink_factor;
  {
#if defined(USE_SKIA)
    SkDevice* device = metafile->StartPageForVectorCanvas(
        *page_size, content_area, scale_factor);
    if (!device)
      return;

    SkRefPtr<skia::VectorCanvas> canvas = new skia::VectorCanvas(device);
    canvas->unref();  // SkRefPtr and new both took a reference.
    WebKit::WebCanvas* canvas_ptr = canvas.get();
    printing::MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile);
    skia::SetIsDraftMode(*canvas, is_print_ready_metafile_sent_);
    skia::SetIsPreviewMetafile(*canvas, is_preview);
#else
    bool success = metafile->StartPage(*page_size, content_area, scale_factor);
    DCHECK(success);
    // printPage can create autoreleased references to |context|. PDF contexts
    // don't write all their data until they are destroyed, so we need to make
    // certain that there are no lingering references.
    base::mac::ScopedNSAutoreleasePool pool;
    CGContextRef cgContext = metafile->context();
    CGContextRef canvas_ptr = cgContext;

    // For CoreGraphics, print in the margins before printing in the content
    // area so that we don't spill over. Webkit draws a white background in the
    // content area and this acts as a clip.
    // TODO(aayushkumar): Combine the calls to PrintHeaderAndFooter once we
    // can draw in the margins safely in Skia in any order.
    if (print_pages_params_->params.display_header_footer) {
      PrintHeaderAndFooter(canvas_ptr, page_number + 1,
                           print_preview_context_.total_page_count(),
                           scale_factor, page_layout_in_points,
                           *header_footer_info_);
    }
#endif  // !USE_SKIA

    frame->printPage(page_number, canvas_ptr);

#if defined(USE_SKIA)
    if (print_pages_params_->params.display_header_footer) {
      // |page_number| is 0-based, so 1 is added.
      PrintHeaderAndFooter(canvas_ptr, page_number + 1,
                           print_preview_context_.total_page_count(),
                           scale_factor, page_layout_in_points,
                           *header_footer_info_);
    }
#endif  // defined(USE_SKIA)
  }

  // Done printing. Close the device context to retrieve the compiled metafile.
  metafile->FinishPage();
}