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 "printing/pdf_metafile_skia.h"
      6 
      7 #include "base/containers/hash_tables.h"
      8 #include "base/file_util.h"
      9 #include "base/metrics/histogram.h"
     10 #include "base/numerics/safe_conversions.h"
     11 #include "base/posix/eintr_wrapper.h"
     12 #include "skia/ext/refptr.h"
     13 #include "skia/ext/vector_platform_device_skia.h"
     14 #include "third_party/skia/include/core/SkData.h"
     15 #include "third_party/skia/include/core/SkRefCnt.h"
     16 #include "third_party/skia/include/core/SkScalar.h"
     17 #include "third_party/skia/include/core/SkStream.h"
     18 #include "third_party/skia/include/core/SkTypeface.h"
     19 #include "third_party/skia/include/pdf/SkPDFDevice.h"
     20 #include "third_party/skia/include/pdf/SkPDFDocument.h"
     21 #include "ui/gfx/point.h"
     22 #include "ui/gfx/rect.h"
     23 #include "ui/gfx/size.h"
     24 
     25 #if defined(OS_MACOSX)
     26 #include "printing/pdf_metafile_cg_mac.h"
     27 #endif
     28 
     29 #if defined(OS_POSIX)
     30 #include "base/file_descriptor_posix.h"
     31 #endif
     32 
     33 namespace printing {
     34 
     35 struct PdfMetafileSkiaData {
     36   skia::RefPtr<SkPDFDevice> current_page_;
     37   SkPDFDocument pdf_doc_;
     38   SkDynamicMemoryWStream pdf_stream_;
     39 #if defined(OS_MACOSX)
     40   PdfMetafileCg pdf_cg_;
     41 #endif
     42 };
     43 
     44 PdfMetafileSkia::~PdfMetafileSkia() {}
     45 
     46 bool PdfMetafileSkia::Init() {
     47   return true;
     48 }
     49 bool PdfMetafileSkia::InitFromData(const void* src_buffer,
     50                                    uint32 src_buffer_size) {
     51   return data_->pdf_stream_.write(src_buffer, src_buffer_size);
     52 }
     53 
     54 SkBaseDevice* PdfMetafileSkia::StartPageForVectorCanvas(
     55     const gfx::Size& page_size, const gfx::Rect& content_area,
     56     const float& scale_factor) {
     57   DCHECK(!page_outstanding_);
     58   page_outstanding_ = true;
     59 
     60   // Adjust for the margins and apply the scale factor.
     61   SkMatrix transform;
     62   transform.setTranslate(SkIntToScalar(content_area.x()),
     63                          SkIntToScalar(content_area.y()));
     64   transform.preScale(SkFloatToScalar(scale_factor),
     65                      SkFloatToScalar(scale_factor));
     66 
     67   SkISize pdf_page_size = SkISize::Make(page_size.width(), page_size.height());
     68   SkISize pdf_content_size =
     69       SkISize::Make(content_area.width(), content_area.height());
     70   skia::RefPtr<SkPDFDevice> pdf_device =
     71       skia::AdoptRef(new skia::VectorPlatformDeviceSkia(
     72           pdf_page_size, pdf_content_size, transform));
     73   data_->current_page_ = pdf_device;
     74   return pdf_device.get();
     75 }
     76 
     77 bool PdfMetafileSkia::StartPage(const gfx::Size& page_size,
     78                                 const gfx::Rect& content_area,
     79                                 const float& scale_factor) {
     80   NOTREACHED();
     81   return false;
     82 }
     83 
     84 bool PdfMetafileSkia::FinishPage() {
     85   DCHECK(data_->current_page_.get());
     86 
     87   data_->pdf_doc_.appendPage(data_->current_page_.get());
     88   page_outstanding_ = false;
     89   return true;
     90 }
     91 
     92 bool PdfMetafileSkia::FinishDocument() {
     93   // Don't do anything if we've already set the data in InitFromData.
     94   if (data_->pdf_stream_.getOffset())
     95     return true;
     96 
     97   if (page_outstanding_)
     98     FinishPage();
     99 
    100   data_->current_page_.clear();
    101 
    102   int font_counts[SkAdvancedTypefaceMetrics::kOther_Font + 2];
    103   data_->pdf_doc_.getCountOfFontTypes(font_counts);
    104   for (int type = 0;
    105        type <= SkAdvancedTypefaceMetrics::kOther_Font + 1;
    106        type++) {
    107     for (int count = 0; count < font_counts[type]; count++) {
    108       UMA_HISTOGRAM_ENUMERATION(
    109           "PrintPreview.FontType", type,
    110           SkAdvancedTypefaceMetrics::kOther_Font + 2);
    111     }
    112   }
    113 
    114   return data_->pdf_doc_.emitPDF(&data_->pdf_stream_);
    115 }
    116 
    117 uint32 PdfMetafileSkia::GetDataSize() const {
    118   return base::checked_cast<uint32>(data_->pdf_stream_.getOffset());
    119 }
    120 
    121 bool PdfMetafileSkia::GetData(void* dst_buffer,
    122                               uint32 dst_buffer_size) const {
    123   if (dst_buffer_size < GetDataSize())
    124     return false;
    125 
    126   SkAutoDataUnref data(data_->pdf_stream_.copyToData());
    127   memcpy(dst_buffer, data->bytes(), dst_buffer_size);
    128   return true;
    129 }
    130 
    131 bool PdfMetafileSkia::SaveTo(const base::FilePath& file_path) const {
    132   DCHECK_GT(data_->pdf_stream_.getOffset(), 0U);
    133   SkAutoDataUnref data(data_->pdf_stream_.copyToData());
    134   if (base::WriteFile(file_path,
    135                       reinterpret_cast<const char*>(data->data()),
    136                       GetDataSize()) != static_cast<int>(GetDataSize())) {
    137     DLOG(ERROR) << "Failed to save file " << file_path.value().c_str();
    138     return false;
    139   }
    140   return true;
    141 }
    142 
    143 gfx::Rect PdfMetafileSkia::GetPageBounds(unsigned int page_number) const {
    144   // TODO(vandebo) add a method to get the page size for a given page to
    145   // SkPDFDocument.
    146   NOTIMPLEMENTED();
    147   return gfx::Rect();
    148 }
    149 
    150 unsigned int PdfMetafileSkia::GetPageCount() const {
    151   // TODO(vandebo) add a method to get the number of pages to SkPDFDocument.
    152   NOTIMPLEMENTED();
    153   return 0;
    154 }
    155 
    156 gfx::NativeDrawingContext PdfMetafileSkia::context() const {
    157   NOTREACHED();
    158   return NULL;
    159 }
    160 
    161 #if defined(OS_WIN)
    162 bool PdfMetafileSkia::Playback(gfx::NativeDrawingContext hdc,
    163                                const RECT* rect) const {
    164   NOTREACHED();
    165   return false;
    166 }
    167 
    168 bool PdfMetafileSkia::SafePlayback(gfx::NativeDrawingContext hdc) const {
    169   NOTREACHED();
    170   return false;
    171 }
    172 
    173 HENHMETAFILE PdfMetafileSkia::emf() const {
    174   NOTREACHED();
    175   return NULL;
    176 }
    177 #elif defined(OS_MACOSX)
    178 /* TODO(caryclark): The set up of PluginInstance::PrintPDFOutput may result in
    179    rasterized output.  Even if that flow uses PdfMetafileCg::RenderPage,
    180    the drawing of the PDF into the canvas may result in a rasterized output.
    181    PDFMetafileSkia::RenderPage should be not implemented as shown and instead
    182    should do something like the following CL in PluginInstance::PrintPDFOutput:
    183 http://codereview.chromium.org/7200040/diff/1/webkit/plugins/ppapi/ppapi_plugin_instance.cc
    184 */
    185 bool PdfMetafileSkia::RenderPage(unsigned int page_number,
    186                                  CGContextRef context,
    187                                  const CGRect rect,
    188                                  const MacRenderPageParams& params) const {
    189   DCHECK_GT(data_->pdf_stream_.getOffset(), 0U);
    190   if (data_->pdf_cg_.GetDataSize() == 0) {
    191     SkAutoDataUnref data(data_->pdf_stream_.copyToData());
    192     data_->pdf_cg_.InitFromData(data->bytes(), data->size());
    193   }
    194   return data_->pdf_cg_.RenderPage(page_number, context, rect, params);
    195 }
    196 #endif
    197 
    198 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
    199 bool PdfMetafileSkia::SaveToFD(const base::FileDescriptor& fd) const {
    200   DCHECK_GT(data_->pdf_stream_.getOffset(), 0U);
    201 
    202   if (fd.fd < 0) {
    203     DLOG(ERROR) << "Invalid file descriptor!";
    204     return false;
    205   }
    206 
    207   bool result = true;
    208   SkAutoDataUnref data(data_->pdf_stream_.copyToData());
    209   if (base::WriteFileDescriptor(fd.fd,
    210                                 reinterpret_cast<const char*>(data->data()),
    211                                 GetDataSize()) !=
    212       static_cast<int>(GetDataSize())) {
    213     DLOG(ERROR) << "Failed to save file with fd " << fd.fd;
    214     result = false;
    215   }
    216 
    217   if (fd.auto_close) {
    218     if (IGNORE_EINTR(close(fd.fd)) < 0) {
    219       DPLOG(WARNING) << "close";
    220       result = false;
    221     }
    222   }
    223   return result;
    224 }
    225 #endif
    226 
    227 PdfMetafileSkia::PdfMetafileSkia()
    228     : data_(new PdfMetafileSkiaData),
    229       page_outstanding_(false) {
    230 }
    231 
    232 PdfMetafileSkia* PdfMetafileSkia::GetMetafileForCurrentPage() {
    233   SkPDFDocument pdf_doc(SkPDFDocument::kDraftMode_Flags);
    234   SkDynamicMemoryWStream pdf_stream;
    235   if (!pdf_doc.appendPage(data_->current_page_.get()))
    236     return NULL;
    237 
    238   if (!pdf_doc.emitPDF(&pdf_stream))
    239     return NULL;
    240 
    241   SkAutoDataUnref data(pdf_stream.copyToData());
    242   if (data->size() == 0)
    243     return NULL;
    244 
    245   PdfMetafileSkia* metafile = new PdfMetafileSkia;
    246   metafile->InitFromData(data->bytes(),
    247                          base::checked_cast<uint32>(data->size()));
    248   return metafile;
    249 }
    250 
    251 }  // namespace printing
    252