Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / printing / pdf_metafile_skia.cc
blob1fb3557611c94ecccd4c57bfeb9a1a4e86b62dbc
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.
5 #include "printing/pdf_metafile_skia.h"
7 #include "base/containers/hash_tables.h"
8 #include "base/files/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 "third_party/skia/include/core/SkData.h"
14 #include "third_party/skia/include/core/SkDocument.h"
15 #include "third_party/skia/include/core/SkPictureRecorder.h"
16 #include "third_party/skia/include/core/SkRect.h"
17 #include "third_party/skia/include/core/SkRefCnt.h"
18 #include "third_party/skia/include/core/SkScalar.h"
19 #include "third_party/skia/include/core/SkSize.h"
20 #include "third_party/skia/include/core/SkStream.h"
21 #include "ui/gfx/geometry/point.h"
22 #include "ui/gfx/geometry/rect.h"
23 #include "ui/gfx/geometry/size.h"
24 #include "ui/gfx/geometry/size_conversions.h"
25 #include "ui/gfx/skia_util.h"
27 #if defined(OS_MACOSX)
28 #include "printing/pdf_metafile_cg_mac.h"
29 #endif
31 #if defined(OS_POSIX)
32 #include "base/file_descriptor_posix.h"
33 #endif
35 namespace {
37 // This struct represents all the data we need to draw and redraw this
38 // page into a SkDocument.
39 struct Page {
40 Page(const SkSize& page_size, const SkRect& content_area, float scale)
41 : page_size_(page_size),
42 content_area_(content_area),
43 scale_factor_(scale),
44 content_(/*NULL*/) {}
45 SkSize page_size_;
46 SkRect content_area_;
47 float scale_factor_;
48 skia::RefPtr<SkPicture> content_;
51 bool WriteAssetToBuffer(const SkStreamAsset* asset,
52 void* buffer,
53 size_t size) {
54 // Calling duplicate() keeps original asset state unchanged.
55 scoped_ptr<SkStreamAsset> assetCopy(asset->duplicate());
56 size_t length = assetCopy->getLength();
57 if (length > size)
58 return false;
59 return (length == assetCopy->read(buffer, length));
62 } // namespace
64 namespace printing {
66 struct PdfMetafileSkiaData {
67 SkPictureRecorder recorder_; // Current recording
69 std::vector<Page> pages_;
70 scoped_ptr<SkStreamAsset> pdf_data_;
72 #if defined(OS_MACOSX)
73 PdfMetafileCg pdf_cg_;
74 #endif
77 PdfMetafileSkia::~PdfMetafileSkia() {}
79 bool PdfMetafileSkia::Init() {
80 return true;
83 // TODO(halcanary): Create a Metafile class that only stores data.
84 // Metafile::InitFromData is orthogonal to what the rest of
85 // PdfMetafileSkia does.
86 bool PdfMetafileSkia::InitFromData(const void* src_buffer,
87 uint32 src_buffer_size) {
88 data_->pdf_data_.reset(new SkMemoryStream(src_buffer, src_buffer_size, true));
89 return true;
92 bool PdfMetafileSkia::StartPage(const gfx::Size& page_size,
93 const gfx::Rect& content_area,
94 const float& scale_factor) {
95 if (data_->recorder_.getRecordingCanvas())
96 this->FinishPage();
97 DCHECK(!data_->recorder_.getRecordingCanvas());
98 SkSize sk_page_size = gfx::SizeFToSkSize(gfx::SizeF(page_size));
99 data_->pages_.push_back(
100 Page(sk_page_size, gfx::RectToSkRect(content_area), scale_factor));
101 DCHECK_GT(scale_factor, 0.0f);
102 // We scale the recording canvas's size so that
103 // canvas->getTotalMatrix() returns a value that ignores the scale
104 // factor. We store the scale factor and re-apply it to the PDF
105 // Canvas later. http://crbug.com/469656
106 // Recording canvas is owned by the data_->recorder_. No ref() necessary.
107 return !!data_->recorder_.beginRecording(sk_page_size.width() / scale_factor,
108 sk_page_size.height() / scale_factor,
109 NULL, 0);
112 skia::PlatformCanvas* PdfMetafileSkia::GetVectorCanvasForNewPage(
113 const gfx::Size& page_size,
114 const gfx::Rect& content_area,
115 const float& scale_factor) {
116 if (!StartPage(page_size, content_area, scale_factor))
117 return nullptr;
118 return data_->recorder_.getRecordingCanvas();
121 bool PdfMetafileSkia::FinishPage() {
122 if (!data_->recorder_.getRecordingCanvas())
123 return false;
124 DCHECK(!(data_->pages_.back().content_));
125 data_->pages_.back().content_ =
126 skia::AdoptRef(data_->recorder_.endRecordingAsPicture());
127 return true;
130 bool PdfMetafileSkia::FinishDocument() {
131 // If we've already set the data in InitFromData, leave it be.
132 if (data_->pdf_data_)
133 return false;
135 if (data_->recorder_.getRecordingCanvas())
136 this->FinishPage();
138 SkDynamicMemoryWStream pdf_stream;
139 skia::RefPtr<SkDocument> pdf_doc =
140 skia::AdoptRef(SkDocument::CreatePDF(&pdf_stream));
141 for (const auto& page : data_->pages_) {
142 SkCanvas* canvas = pdf_doc->beginPage(
143 page.page_size_.width(), page.page_size_.height(), &page.content_area_);
144 // No need to save/restore, since this canvas is not reused after endPage()
145 canvas->scale(page.scale_factor_, page.scale_factor_);
146 canvas->drawPicture(page.content_.get());
147 pdf_doc->endPage();
149 if (!pdf_doc->close())
150 return false;
152 data_->pdf_data_.reset(pdf_stream.detachAsStream());
153 return true;
156 uint32 PdfMetafileSkia::GetDataSize() const {
157 if (!data_->pdf_data_)
158 return 0;
159 return base::checked_cast<uint32>(data_->pdf_data_->getLength());
162 bool PdfMetafileSkia::GetData(void* dst_buffer,
163 uint32 dst_buffer_size) const {
164 if (!data_->pdf_data_)
165 return false;
166 return WriteAssetToBuffer(data_->pdf_data_.get(), dst_buffer,
167 base::checked_cast<size_t>(dst_buffer_size));
170 gfx::Rect PdfMetafileSkia::GetPageBounds(unsigned int page_number) const {
171 if (page_number < data_->pages_.size()) {
172 return gfx::Rect(gfx::ToFlooredSize(
173 gfx::SkSizeToSizeF(data_->pages_[page_number].page_size_)));
175 return gfx::Rect();
178 unsigned int PdfMetafileSkia::GetPageCount() const {
179 return base::checked_cast<unsigned int>(data_->pages_.size());
182 gfx::NativeDrawingContext PdfMetafileSkia::context() const {
183 NOTREACHED();
184 return NULL;
187 #if defined(OS_WIN)
188 bool PdfMetafileSkia::Playback(gfx::NativeDrawingContext hdc,
189 const RECT* rect) const {
190 NOTREACHED();
191 return false;
194 bool PdfMetafileSkia::SafePlayback(gfx::NativeDrawingContext hdc) const {
195 NOTREACHED();
196 return false;
199 #elif defined(OS_MACOSX)
200 /* TODO(caryclark): The set up of PluginInstance::PrintPDFOutput may result in
201 rasterized output. Even if that flow uses PdfMetafileCg::RenderPage,
202 the drawing of the PDF into the canvas may result in a rasterized output.
203 PDFMetafileSkia::RenderPage should be not implemented as shown and instead
204 should do something like the following CL in PluginInstance::PrintPDFOutput:
205 http://codereview.chromium.org/7200040/diff/1/webkit/plugins/ppapi/ppapi_plugin_instance.cc
207 bool PdfMetafileSkia::RenderPage(unsigned int page_number,
208 CGContextRef context,
209 const CGRect rect,
210 const MacRenderPageParams& params) const {
211 DCHECK_GT(GetDataSize(), 0U);
212 if (data_->pdf_cg_.GetDataSize() == 0) {
213 if (GetDataSize() == 0)
214 return false;
215 size_t length = data_->pdf_data_->getLength();
216 std::vector<uint8_t> buffer(length);
217 (void)WriteAssetToBuffer(data_->pdf_data_.get(), &buffer[0], length);
218 data_->pdf_cg_.InitFromData(&buffer[0], base::checked_cast<uint32>(length));
220 return data_->pdf_cg_.RenderPage(page_number, context, rect, params);
222 #endif
224 bool PdfMetafileSkia::SaveTo(base::File* file) const {
225 if (GetDataSize() == 0U)
226 return false;
228 // Calling duplicate() keeps original asset state unchanged.
229 scoped_ptr<SkStreamAsset> asset(data_->pdf_data_->duplicate());
231 const size_t maximum_buffer_size = 1024 * 1024;
232 std::vector<char> buffer(std::min(maximum_buffer_size, asset->getLength()));
233 do {
234 size_t read_size = asset->read(&buffer[0], buffer.size());
235 if (read_size == 0)
236 break;
237 DCHECK_GE(buffer.size(), read_size);
238 if (!file->WriteAtCurrentPos(&buffer[0],
239 base::checked_cast<int>(read_size))) {
240 return false;
242 } while (!asset->isAtEnd());
244 return true;
247 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
248 bool PdfMetafileSkia::SaveToFD(const base::FileDescriptor& fd) const {
249 DCHECK_GT(GetDataSize(), 0U);
251 if (fd.fd < 0) {
252 DLOG(ERROR) << "Invalid file descriptor!";
253 return false;
255 base::File file(fd.fd);
256 bool result = SaveTo(&file);
257 DLOG_IF(ERROR, !result) << "Failed to save file with fd " << fd.fd;
259 if (!fd.auto_close)
260 file.TakePlatformFile();
261 return result;
263 #endif
265 PdfMetafileSkia::PdfMetafileSkia() : data_(new PdfMetafileSkiaData) {
268 scoped_ptr<PdfMetafileSkia> PdfMetafileSkia::GetMetafileForCurrentPage() {
269 // If we only ever need the metafile for the last page, should we
270 // only keep a handle on one SkPicture?
271 scoped_ptr<PdfMetafileSkia> metafile(new PdfMetafileSkia);
273 if (data_->pages_.size() == 0)
274 return metafile.Pass();
276 if (data_->recorder_.getRecordingCanvas()) // page outstanding
277 return metafile.Pass();
279 const Page& page = data_->pages_.back();
281 metafile->data_->pages_.push_back(page);
283 if (!metafile->FinishDocument()) // Generate PDF.
284 metafile.reset();
286 return metafile.Pass();
289 } // namespace printing