Home | History | Annotate | Download | only in printing
      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
      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/metrics/histogram.h"
     10 #include "base/process/process_handle.h"
     11 #include "base/win/scoped_gdi_object.h"
     12 #include "base/win/scoped_hdc.h"
     13 #include "base/win/scoped_select_object.h"
     14 #include "chrome/common/print_messages.h"
     15 #include "printing/metafile.h"
     16 #include "printing/metafile_impl.h"
     17 #include "printing/metafile_skia_wrapper.h"
     18 #include "printing/page_size_margins.h"
     19 #include "printing/units.h"
     20 #include "skia/ext/platform_device.h"
     21 #include "skia/ext/refptr.h"
     22 #include "skia/ext/vector_canvas.h"
     23 #include "third_party/WebKit/public/web/WebLocalFrame.h"
     24 #include "ui/gfx/gdi_util.h"
     25 #include "ui/gfx/point.h"
     26 #include "ui/gfx/rect.h"
     27 #include "ui/gfx/size.h"
     28 
     29 namespace printing {
     30 
     31 using blink::WebFrame;
     32 
     33 void PrintWebViewHelper::PrintPageInternal(
     34     const PrintMsg_PrintPage_Params& params,
     35     const gfx::Size& canvas_size,
     36     WebFrame* frame) {
     37   // Generate a memory-based metafile. It will use the current screen's DPI.
     38   // Each metafile contains a single page.
     39   scoped_ptr<NativeMetafile> metafile(new NativeMetafile);
     40   metafile->Init();
     41   DCHECK(metafile->context());
     42   skia::InitializeDC(metafile->context());
     43 
     44   int page_number = params.page_number;
     45 
     46   // Calculate the dpi adjustment.
     47   // Browser will render context using desired_dpi, so we need to calculate
     48   // adjustment factor to play content on the printer DC later during the
     49   // actual printing.
     50   double actual_shrink = static_cast<float>(params.params.desired_dpi /
     51                                             params.params.dpi);
     52   gfx::Size page_size_in_dpi;
     53   gfx::Rect content_area_in_dpi;
     54 
     55   // Render page for printing.
     56   RenderPage(params.params, page_number, frame, false, metafile.get(),
     57              &actual_shrink, &page_size_in_dpi, &content_area_in_dpi);
     58 
     59   // Close the device context to retrieve the compiled metafile.
     60   if (!metafile->FinishDocument())
     61     NOTREACHED();
     62 
     63   if (!params.params.supports_alpha_blend && metafile->IsAlphaBlendUsed()) {
     64     scoped_ptr<NativeMetafile> raster_metafile(
     65         metafile->RasterizeAlphaBlend());
     66     if (raster_metafile.get())
     67       metafile.swap(raster_metafile);
     68   }
     69 
     70   // Get the size of the compiled metafile.
     71   uint32 buf_size = metafile->GetDataSize();
     72   DCHECK_GT(buf_size, 128u);
     73 
     74   PrintHostMsg_DidPrintPage_Params page_params;
     75   page_params.data_size = buf_size;
     76   page_params.metafile_data_handle = NULL;
     77   page_params.page_number = page_number;
     78   page_params.document_cookie = params.params.document_cookie;
     79   page_params.actual_shrink = actual_shrink;
     80   page_params.page_size = page_size_in_dpi;
     81   page_params.content_area = content_area_in_dpi;
     82 
     83   if (!CopyMetafileDataToSharedMem(metafile.get(),
     84                                    &(page_params.metafile_data_handle))) {
     85     page_params.data_size = 0;
     86   }
     87 
     88   Send(new PrintHostMsg_DidPrintPage(routing_id(), page_params));
     89 }
     90 
     91 bool PrintWebViewHelper::RenderPreviewPage(
     92     int page_number,
     93     const PrintMsg_Print_Params& print_params) {
     94   // Calculate the dpi adjustment.
     95   double actual_shrink = static_cast<float>(print_params.desired_dpi /
     96                                             print_params.dpi);
     97   scoped_ptr<Metafile> draft_metafile;
     98   Metafile* initial_render_metafile = print_preview_context_.metafile();
     99 
    100   if (print_preview_context_.IsModifiable() && is_print_ready_metafile_sent_) {
    101     draft_metafile.reset(new PreviewMetafile);
    102     initial_render_metafile = draft_metafile.get();
    103   }
    104 
    105   base::TimeTicks begin_time = base::TimeTicks::Now();
    106   RenderPage(print_params, page_number, print_preview_context_.prepared_frame(),
    107              true, initial_render_metafile, &actual_shrink, NULL, NULL);
    108   print_preview_context_.RenderedPreviewPage(
    109       base::TimeTicks::Now() - begin_time);
    110 
    111   if (draft_metafile.get()) {
    112     draft_metafile->FinishDocument();
    113   } else if (print_preview_context_.IsModifiable() &&
    114              print_preview_context_.generate_draft_pages()) {
    115     DCHECK(!draft_metafile.get());
    116     draft_metafile.reset(
    117         print_preview_context_.metafile()->GetMetafileForCurrentPage());
    118   }
    119   return PreviewPageRendered(page_number, draft_metafile.get());
    120 }
    121 
    122 void PrintWebViewHelper::RenderPage(
    123     const PrintMsg_Print_Params& params, int page_number, WebFrame* frame,
    124     bool is_preview, Metafile* metafile, double* actual_shrink,
    125     gfx::Size* page_size_in_dpi, gfx::Rect* content_area_in_dpi) {
    126   PageSizeMargins page_layout_in_points;
    127   double css_scale_factor = 1.0f;
    128   ComputePageLayoutInPointsForCss(frame, page_number, params,
    129                                   ignore_css_margins_, &css_scale_factor,
    130                                   &page_layout_in_points);
    131   gfx::Size page_size;
    132   gfx::Rect content_area;
    133   GetPageSizeAndContentAreaFromPageLayout(page_layout_in_points, &page_size,
    134                                           &content_area);
    135   int dpi = static_cast<int>(params.dpi);
    136   // Calculate the actual page size and content area in dpi.
    137   if (page_size_in_dpi) {
    138     *page_size_in_dpi = gfx::Size(
    139         static_cast<int>(ConvertUnitDouble(page_size.width(), kPointsPerInch,
    140                                            dpi)),
    141         static_cast<int>(ConvertUnitDouble(page_size.height(), kPointsPerInch,
    142                                            dpi)));
    143   }
    144 
    145   if (content_area_in_dpi) {
    146     *content_area_in_dpi = gfx::Rect(
    147         static_cast<int>(ConvertUnitDouble(content_area.x(), kPointsPerInch,
    148                                            dpi)),
    149         static_cast<int>(ConvertUnitDouble(content_area.y(), kPointsPerInch,
    150                                            dpi)),
    151         static_cast<int>(ConvertUnitDouble(content_area.width(), kPointsPerInch,
    152                                            dpi)),
    153         static_cast<int>(ConvertUnitDouble(content_area.height(),
    154                                            kPointsPerInch, dpi)));
    155   }
    156 
    157   if (!is_preview) {
    158     // Since WebKit extends the page width depending on the magical scale factor
    159     // we make sure the canvas covers the worst case scenario (x2.0 currently).
    160     // PrintContext will then set the correct clipping region.
    161     page_size = gfx::Size(
    162         static_cast<int>(page_layout_in_points.content_width *
    163                          params.max_shrink),
    164         static_cast<int>(page_layout_in_points.content_height *
    165                          params.max_shrink));
    166   }
    167 
    168   float webkit_page_shrink_factor = frame->getPrintPageShrink(page_number);
    169   float scale_factor = css_scale_factor * webkit_page_shrink_factor;
    170 
    171   gfx::Rect canvas_area =
    172       params.display_header_footer ? gfx::Rect(page_size) : content_area;
    173 
    174   SkBaseDevice* device = metafile->StartPageForVectorCanvas(
    175       page_size, canvas_area, scale_factor);
    176   DCHECK(device);
    177   // The printPage method may take a reference to the canvas we pass down, so it
    178   // can't be a stack object.
    179   skia::RefPtr<skia::VectorCanvas> canvas =
    180       skia::AdoptRef(new skia::VectorCanvas(device));
    181 
    182   if (is_preview) {
    183     MetafileSkiaWrapper::SetMetafileOnCanvas(*canvas, metafile);
    184     skia::SetIsDraftMode(*canvas, is_print_ready_metafile_sent_);
    185     skia::SetIsPreviewMetafile(*canvas, is_preview);
    186   }
    187 
    188   if (params.display_header_footer) {
    189     // |page_number| is 0-based, so 1 is added.
    190     PrintHeaderAndFooter(canvas.get(), page_number + 1,
    191         print_preview_context_.total_page_count(), scale_factor,
    192         page_layout_in_points, *header_footer_info_, params);
    193   }
    194 
    195   float webkit_scale_factor = RenderPageContent(frame, page_number, canvas_area,
    196                                                 content_area, scale_factor,
    197                                                 canvas.get());
    198 
    199   if (*actual_shrink <= 0 || webkit_scale_factor <= 0) {
    200     NOTREACHED() << "Printing page " << page_number << " failed.";
    201   } else {
    202     // While rendering certain plugins (PDF) to metafile, we might need to
    203     // set custom scale factor. Update |actual_shrink| with custom scale
    204     // if it is set on canvas.
    205     // TODO(gene): We should revisit this solution for the next versions.
    206     // Consider creating metafile of the right size (or resizable)
    207     // https://code.google.com/p/chromium/issues/detail?id=126037
    208     if (!MetafileSkiaWrapper::GetCustomScaleOnCanvas(
    209             *canvas, actual_shrink)) {
    210       // Update the dpi adjustment with the "page |actual_shrink|" calculated in
    211       // webkit.
    212       *actual_shrink /= (webkit_scale_factor * css_scale_factor);
    213     }
    214   }
    215 
    216   bool result = metafile->FinishPage();
    217   DCHECK(result);
    218 }
    219 
    220 bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
    221     Metafile* metafile, base::SharedMemoryHandle* shared_mem_handle) {
    222   uint32 buf_size = metafile->GetDataSize();
    223   base::SharedMemory shared_buf;
    224   if (buf_size >= kMetafileMaxSize) {
    225     NOTREACHED() << "Buffer too large: " << buf_size;
    226     return false;
    227   }
    228 
    229   // Allocate a shared memory buffer to hold the generated metafile data.
    230   if (!shared_buf.CreateAndMapAnonymous(buf_size)) {
    231     NOTREACHED() << "Buffer allocation failed";
    232     return false;
    233   }
    234 
    235   // Copy the bits into shared memory.
    236   if (!metafile->GetData(shared_buf.memory(), buf_size)) {
    237     NOTREACHED() << "GetData() failed";
    238     shared_buf.Unmap();
    239     return false;
    240   }
    241   shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), shared_mem_handle);
    242   shared_buf.Unmap();
    243 
    244   Send(new PrintHostMsg_DuplicateSection(routing_id(), *shared_mem_handle,
    245                                          shared_mem_handle));
    246   return true;
    247 }
    248 
    249 }  // namespace printing
    250