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