blob: 1fb3557611c94ecccd4c57bfeb9a1a4e86b62dbc [file] [log] [blame]
[email protected]0a4392a2012-03-23 17:50:191// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]8f879292011-04-08 00:21:202// 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
[email protected]14c1c232013-06-11 17:52:447#include "base/containers/hash_tables.h"
thestig22dfc4012014-09-05 08:29:448#include "base/files/file_util.h"
[email protected]67e16b392011-05-30 20:58:099#include "base/metrics/histogram.h"
[email protected]cb154062014-01-17 03:32:4010#include "base/numerics/safe_conversions.h"
[email protected]2025d002012-11-14 20:54:3511#include "base/posix/eintr_wrapper.h"
[email protected]584abe72013-05-18 09:40:2612#include "skia/ext/refptr.h"
[email protected]e195fbf52011-06-27 16:51:2013#include "third_party/skia/include/core/SkData.h"
halcanaryff214762015-01-22 20:34:3214#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"
[email protected]8f879292011-04-08 00:21:2017#include "third_party/skia/include/core/SkRefCnt.h"
[email protected]a34f0122011-04-12 17:36:3918#include "third_party/skia/include/core/SkScalar.h"
halcanaryff214762015-01-22 20:34:3219#include "third_party/skia/include/core/SkSize.h"
[email protected]8f879292011-04-08 00:21:2020#include "third_party/skia/include/core/SkStream.h"
tfarina655f81d2014-12-23 02:38:5021#include "ui/gfx/geometry/point.h"
tfarina3b0452d2014-12-31 15:20:0922#include "ui/gfx/geometry/rect.h"
tfarinaebe974f02015-01-03 04:25:3223#include "ui/gfx/geometry/size.h"
halcanaryff214762015-01-22 20:34:3224#include "ui/gfx/geometry/size_conversions.h"
25#include "ui/gfx/skia_util.h"
[email protected]8f879292011-04-08 00:21:2026
[email protected]b8d85bc2011-06-22 13:34:5727#if defined(OS_MACOSX)
28#include "printing/pdf_metafile_cg_mac.h"
29#endif
30
[email protected]0daaebfe2014-03-15 00:09:0531#if defined(OS_POSIX)
32#include "base/file_descriptor_posix.h"
33#endif
34
halcanaryff214762015-01-22 20:34:3235namespace {
thestige3f5f5d42015-07-16 19:46:2436
halcanaryff214762015-01-22 20:34:3237// This struct represents all the data we need to draw and redraw this
38// page into a SkDocument.
39struct Page {
halcanary5baa8fb42015-04-03 21:39:3040 Page(const SkSize& page_size, const SkRect& content_area, float scale)
halcanaryff214762015-01-22 20:34:3241 : page_size_(page_size),
42 content_area_(content_area),
halcanary5baa8fb42015-04-03 21:39:3043 scale_factor_(scale),
halcanaryff214762015-01-22 20:34:3244 content_(/*NULL*/) {}
45 SkSize page_size_;
46 SkRect content_area_;
halcanary5baa8fb42015-04-03 21:39:3047 float scale_factor_;
halcanaryff214762015-01-22 20:34:3248 skia::RefPtr<SkPicture> content_;
49};
halcanaryff214762015-01-22 20:34:3250
thestige3f5f5d42015-07-16 19:46:2451bool WriteAssetToBuffer(const SkStreamAsset* asset,
52 void* buffer,
53 size_t size) {
halcanaryff214762015-01-22 20:34:3254 // 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));
60}
61
thestige3f5f5d42015-07-16 19:46:2462} // namespace
63
[email protected]8f879292011-04-08 00:21:2064namespace printing {
65
66struct PdfMetafileSkiaData {
halcanaryff214762015-01-22 20:34:3267 SkPictureRecorder recorder_; // Current recording
68
69 std::vector<Page> pages_;
70 scoped_ptr<SkStreamAsset> pdf_data_;
71
[email protected]b8d85bc2011-06-22 13:34:5772#if defined(OS_MACOSX)
73 PdfMetafileCg pdf_cg_;
74#endif
[email protected]8f879292011-04-08 00:21:2075};
76
77PdfMetafileSkia::~PdfMetafileSkia() {}
78
79bool PdfMetafileSkia::Init() {
80 return true;
81}
halcanaryff214762015-01-22 20:34:3282
83// TODO(halcanary): Create a Metafile class that only stores data.
84// Metafile::InitFromData is orthogonal to what the rest of
85// PdfMetafileSkia does.
[email protected]8f879292011-04-08 00:21:2086bool PdfMetafileSkia::InitFromData(const void* src_buffer,
87 uint32 src_buffer_size) {
halcanaryff214762015-01-22 20:34:3288 data_->pdf_data_.reset(new SkMemoryStream(src_buffer, src_buffer_size, true));
89 return true;
[email protected]8f879292011-04-08 00:21:2090}
91
halcanary5be808e2014-11-10 22:20:0592bool PdfMetafileSkia::StartPage(const gfx::Size& page_size,
93 const gfx::Rect& content_area,
94 const float& scale_factor) {
halcanaryff214762015-01-22 20:34:3295 if (data_->recorder_.getRecordingCanvas())
96 this->FinishPage();
97 DCHECK(!data_->recorder_.getRecordingCanvas());
98 SkSize sk_page_size = gfx::SizeFToSkSize(gfx::SizeF(page_size));
halcanary5baa8fb42015-04-03 21:39:3099 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);
[email protected]8f879292011-04-08 00:21:20110}
111
vitalybukaa73fc372015-02-06 18:40:37112skia::PlatformCanvas* PdfMetafileSkia::GetVectorCanvasForNewPage(
halcanary5be808e2014-11-10 22:20:05113 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;
halcanaryff214762015-01-22 20:34:32118 return data_->recorder_.getRecordingCanvas();
[email protected]8f879292011-04-08 00:21:20119}
120
121bool PdfMetafileSkia::FinishPage() {
halcanaryff214762015-01-22 20:34:32122 if (!data_->recorder_.getRecordingCanvas())
123 return false;
124 DCHECK(!(data_->pages_.back().content_));
125 data_->pages_.back().content_ =
thestig3523bf82015-04-08 18:49:00126 skia::AdoptRef(data_->recorder_.endRecordingAsPicture());
[email protected]8f879292011-04-08 00:21:20127 return true;
128}
129
130bool PdfMetafileSkia::FinishDocument() {
halcanaryff214762015-01-22 20:34:32131 // If we've already set the data in InitFromData, leave it be.
132 if (data_->pdf_data_)
133 return false;
[email protected]8f879292011-04-08 00:21:20134
halcanaryff214762015-01-22 20:34:32135 if (data_->recorder_.getRecordingCanvas())
136 this->FinishPage();
[email protected]67e16b392011-05-30 20:58:09137
halcanaryff214762015-01-22 20:34:32138 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_);
halcanary5baa8fb42015-04-03 21:39:30144 // No need to save/restore, since this canvas is not reused after endPage()
145 canvas->scale(page.scale_factor_, page.scale_factor_);
halcanaryff214762015-01-22 20:34:32146 canvas->drawPicture(page.content_.get());
147 pdf_doc->endPage();
[email protected]67e16b392011-05-30 20:58:09148 }
halcanaryff214762015-01-22 20:34:32149 if (!pdf_doc->close())
150 return false;
[email protected]67e16b392011-05-30 20:58:09151
halcanaryff214762015-01-22 20:34:32152 data_->pdf_data_.reset(pdf_stream.detachAsStream());
153 return true;
[email protected]8f879292011-04-08 00:21:20154}
155
156uint32 PdfMetafileSkia::GetDataSize() const {
halcanaryff214762015-01-22 20:34:32157 if (!data_->pdf_data_)
158 return 0;
159 return base::checked_cast<uint32>(data_->pdf_data_->getLength());
[email protected]8f879292011-04-08 00:21:20160}
161
162bool PdfMetafileSkia::GetData(void* dst_buffer,
163 uint32 dst_buffer_size) const {
halcanaryff214762015-01-22 20:34:32164 if (!data_->pdf_data_)
[email protected]8f879292011-04-08 00:21:20165 return false;
halcanaryff214762015-01-22 20:34:32166 return WriteAssetToBuffer(data_->pdf_data_.get(), dst_buffer,
167 base::checked_cast<size_t>(dst_buffer_size));
[email protected]8f879292011-04-08 00:21:20168}
169
[email protected]8f879292011-04-08 00:21:20170gfx::Rect PdfMetafileSkia::GetPageBounds(unsigned int page_number) const {
halcanaryff214762015-01-22 20:34:32171 if (page_number < data_->pages_.size()) {
172 return gfx::Rect(gfx::ToFlooredSize(
173 gfx::SkSizeToSizeF(data_->pages_[page_number].page_size_)));
174 }
[email protected]8f879292011-04-08 00:21:20175 return gfx::Rect();
176}
177
178unsigned int PdfMetafileSkia::GetPageCount() const {
halcanaryff214762015-01-22 20:34:32179 return base::checked_cast<unsigned int>(data_->pages_.size());
[email protected]8f879292011-04-08 00:21:20180}
181
182gfx::NativeDrawingContext PdfMetafileSkia::context() const {
183 NOTREACHED();
184 return NULL;
185}
186
187#if defined(OS_WIN)
188bool PdfMetafileSkia::Playback(gfx::NativeDrawingContext hdc,
189 const RECT* rect) const {
190 NOTREACHED();
191 return false;
192}
193
194bool PdfMetafileSkia::SafePlayback(gfx::NativeDrawingContext hdc) const {
195 NOTREACHED();
196 return false;
197}
198
[email protected]b8d85bc2011-06-22 13:34:57199#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:
205http://codereview.chromium.org/7200040/diff/1/webkit/plugins/ppapi/ppapi_plugin_instance.cc
206*/
207bool PdfMetafileSkia::RenderPage(unsigned int page_number,
208 CGContextRef context,
209 const CGRect rect,
[email protected]b5cf844c2012-06-18 21:49:20210 const MacRenderPageParams& params) const {
halcanaryff214762015-01-22 20:34:32211 DCHECK_GT(GetDataSize(), 0U);
[email protected]e195fbf52011-06-27 16:51:20212 if (data_->pdf_cg_.GetDataSize() == 0) {
halcanaryff214762015-01-22 20:34:32213 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));
[email protected]e195fbf52011-06-27 16:51:20219 }
[email protected]b5cf844c2012-06-18 21:49:20220 return data_->pdf_cg_.RenderPage(page_number, context, rect, params);
[email protected]b8d85bc2011-06-22 13:34:57221}
222#endif
[email protected]8f879292011-04-08 00:21:20223
halcanary5be808e2014-11-10 22:20:05224bool PdfMetafileSkia::SaveTo(base::File* file) const {
225 if (GetDataSize() == 0U)
226 return false;
halcanaryff214762015-01-22 20:34:32227
228 // Calling duplicate() keeps original asset state unchanged.
229 scoped_ptr<SkStreamAsset> asset(data_->pdf_data_->duplicate());
230
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;
241 }
242 } while (!asset->isAtEnd());
243
244 return true;
halcanary5be808e2014-11-10 22:20:05245}
246
[email protected]b25f0032013-08-19 22:26:25247#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
[email protected]8f879292011-04-08 00:21:20248bool PdfMetafileSkia::SaveToFD(const base::FileDescriptor& fd) const {
halcanaryff214762015-01-22 20:34:32249 DCHECK_GT(GetDataSize(), 0U);
[email protected]8f879292011-04-08 00:21:20250
251 if (fd.fd < 0) {
252 DLOG(ERROR) << "Invalid file descriptor!";
253 return false;
254 }
vitalybuka5d1290582014-09-12 09:19:59255 base::File file(fd.fd);
halcanary5be808e2014-11-10 22:20:05256 bool result = SaveTo(&file);
vitalybuka5d1290582014-09-12 09:19:59257 DLOG_IF(ERROR, !result) << "Failed to save file with fd " << fd.fd;
[email protected]8f879292011-04-08 00:21:20258
vitalybuka5d1290582014-09-12 09:19:59259 if (!fd.auto_close)
260 file.TakePlatformFile();
[email protected]8f879292011-04-08 00:21:20261 return result;
262}
263#endif
264
halcanary5be808e2014-11-10 22:20:05265PdfMetafileSkia::PdfMetafileSkia() : data_(new PdfMetafileSkiaData) {
[email protected]19b9d3b2011-07-23 02:08:57266}
[email protected]597516372011-07-01 05:10:44267
vitalybuka5d1290582014-09-12 09:19:59268scoped_ptr<PdfMetafileSkia> PdfMetafileSkia::GetMetafileForCurrentPage() {
halcanaryff214762015-01-22 20:34:32269 // 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);
272
273 if (data_->pages_.size() == 0)
vitalybuka5d1290582014-09-12 09:19:59274 return metafile.Pass();
[email protected]597516372011-07-01 05:10:44275
halcanaryff214762015-01-22 20:34:32276 if (data_->recorder_.getRecordingCanvas()) // page outstanding
vitalybuka5d1290582014-09-12 09:19:59277 return metafile.Pass();
[email protected]597516372011-07-01 05:10:44278
halcanaryff214762015-01-22 20:34:32279 const Page& page = data_->pages_.back();
[email protected]597516372011-07-01 05:10:44280
halcanaryff214762015-01-22 20:34:32281 metafile->data_->pages_.push_back(page);
282
283 if (!metafile->FinishDocument()) // Generate PDF.
vitalybuka5d1290582014-09-12 09:19:59284 metafile.reset();
halcanaryff214762015-01-22 20:34:32285
vitalybuka5d1290582014-09-12 09:19:59286 return metafile.Pass();
[email protected]597516372011-07-01 05:10:44287}
[email protected]8f879292011-04-08 00:21:20288
[email protected]8f879292011-04-08 00:21:20289} // namespace printing