blob: aebd263025f66a4ce302d9b8a5fa327595eabacd [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
halcanary223f70a2016-06-09 00:07:427#include "base/files/file.h"
halcanary67ce00b82015-09-27 21:59:538#include "base/time/time.h"
halcanary73b63fd2015-11-06 00:02:149#include "printing/print_settings.h"
halcanaryff214762015-01-22 20:34:3210#include "third_party/skia/include/core/SkDocument.h"
11#include "third_party/skia/include/core/SkPictureRecorder.h"
[email protected]8f879292011-04-08 00:21:2012#include "third_party/skia/include/core/SkStream.h"
halcanary223f70a2016-06-09 00:07:4213#include "ui/gfx/geometry/safe_integer_conversions.h"
halcanaryff214762015-01-22 20:34:3214#include "ui/gfx/skia_util.h"
[email protected]8f879292011-04-08 00:21:2015
[email protected]b8d85bc2011-06-22 13:34:5716#if defined(OS_MACOSX)
17#include "printing/pdf_metafile_cg_mac.h"
18#endif
19
[email protected]0daaebfe2014-03-15 00:09:0520#if defined(OS_POSIX)
21#include "base/file_descriptor_posix.h"
22#endif
23
halcanaryff214762015-01-22 20:34:3224namespace {
thestige3f5f5d42015-07-16 19:46:2425
thestige3f5f5d42015-07-16 19:46:2426bool WriteAssetToBuffer(const SkStreamAsset* asset,
27 void* buffer,
28 size_t size) {
halcanaryff214762015-01-22 20:34:3229 // Calling duplicate() keeps original asset state unchanged.
dchengc3df9ba2016-04-07 23:09:3230 std::unique_ptr<SkStreamAsset> assetCopy(asset->duplicate());
halcanaryff214762015-01-22 20:34:3231 size_t length = assetCopy->getLength();
32 if (length > size)
33 return false;
34 return (length == assetCopy->read(buffer, length));
35}
36
thestig5ff7db22016-02-13 04:42:4537SkTime::DateTime TimeToSkTime(base::Time time) {
halcanary223f70a2016-06-09 00:07:4238 base::Time::Exploded exploded;
39 time.UTCExplode(&exploded);
40 SkTime::DateTime skdate;
41 skdate.fTimeZoneMinutes = 0;
42 skdate.fYear = exploded.year;
43 skdate.fMonth = exploded.month;
44 skdate.fDayOfWeek = exploded.day_of_week;
45 skdate.fDay = exploded.day_of_month;
46 skdate.fHour = exploded.hour;
47 skdate.fMinute = exploded.minute;
48 skdate.fSecond = exploded.second;
49 return skdate;
50}
51
52sk_sp<SkDocument> MakePdfDocument(SkWStream* wStream) {
53 SkDocument::PDFMetadata metadata;
54 SkTime::DateTime now = TimeToSkTime(base::Time::Now());
55 metadata.fCreation.fEnabled = true;
56 metadata.fCreation.fDateTime = now;
57 metadata.fModified.fEnabled = true;
58 metadata.fModified.fDateTime = now;
59 const std::string& agent = printing::GetAgent();
60 metadata.fCreator = agent.empty() ? SkString("Chromium")
61 : SkString(agent.c_str(), agent.size());
62 return SkDocument::MakePDF(wStream, SK_ScalarDefaultRasterDPI, metadata,
63 nullptr, false);
thestig5ff7db22016-02-13 04:42:4564}
65
thestige3f5f5d42015-07-16 19:46:2466} // namespace
67
[email protected]8f879292011-04-08 00:21:2068namespace printing {
69
halcanary8dd6f0242016-06-13 19:40:2370struct Page {
71 Page(SkSize s, sk_sp<SkPicture> c) : size_(s), content_(std::move(c)) {}
72 Page(Page&& that) : size_(that.size_), content_(std::move(that.content_)) {}
73 Page(const Page&) = default;
74 Page& operator=(const Page&) = default;
75 Page& operator=(Page&& that) {
76 size_ = that.size_;
77 content_ = std::move(that.content_);
78 return *this;
79 }
80 SkSize size_;
81 sk_sp<SkPicture> content_;
82};
83
[email protected]8f879292011-04-08 00:21:2084struct PdfMetafileSkiaData {
halcanaryff214762015-01-22 20:34:3285 SkPictureRecorder recorder_; // Current recording
86
halcanary8dd6f0242016-06-13 19:40:2387 std::vector<Page> pages_;
dchengc3df9ba2016-04-07 23:09:3288 std::unique_ptr<SkStreamAsset> pdf_data_;
halcanaryff214762015-01-22 20:34:3289
halcanary223f70a2016-06-09 00:07:4290 // The scale factor is used because Blink occasionally calls
91 // SkCanvas::getTotalMatrix() even though the total matrix is not as
92 // meaningful for a vector canvas as for a raster canvas.
93 float scale_factor_;
halcanary8dd6f0242016-06-13 19:40:2394 SkSize size_;
halcanary223f70a2016-06-09 00:07:4295
[email protected]b8d85bc2011-06-22 13:34:5796#if defined(OS_MACOSX)
97 PdfMetafileCg pdf_cg_;
98#endif
[email protected]8f879292011-04-08 00:21:2099};
100
101PdfMetafileSkia::~PdfMetafileSkia() {}
102
103bool PdfMetafileSkia::Init() {
104 return true;
105}
halcanaryff214762015-01-22 20:34:32106
107// TODO(halcanary): Create a Metafile class that only stores data.
108// Metafile::InitFromData is orthogonal to what the rest of
109// PdfMetafileSkia does.
[email protected]8f879292011-04-08 00:21:20110bool PdfMetafileSkia::InitFromData(const void* src_buffer,
thestig707a24b22015-09-14 18:16:33111 uint32_t src_buffer_size) {
halcanaryff214762015-01-22 20:34:32112 data_->pdf_data_.reset(new SkMemoryStream(src_buffer, src_buffer_size, true));
113 return true;
[email protected]8f879292011-04-08 00:21:20114}
115
thestig192677ec2016-06-09 07:43:17116void PdfMetafileSkia::StartPage(const gfx::Size& page_size,
halcanary5be808e2014-11-10 22:20:05117 const gfx::Rect& content_area,
118 const float& scale_factor) {
halcanary223f70a2016-06-09 00:07:42119 DCHECK_GT(page_size.width(), 0);
120 DCHECK_GT(page_size.height(), 0);
121 DCHECK_GT(scale_factor, 0.0f);
halcanaryff214762015-01-22 20:34:32122 if (data_->recorder_.getRecordingCanvas())
thestig5ff7db22016-02-13 04:42:45123 FinishPage();
halcanaryff214762015-01-22 20:34:32124 DCHECK(!data_->recorder_.getRecordingCanvas());
halcanary223f70a2016-06-09 00:07:42125
126 float inverse_scale = 1.0 / scale_factor;
127 SkCanvas* canvas = data_->recorder_.beginRecording(
128 inverse_scale * page_size.width(), inverse_scale * page_size.height());
129 if (content_area != gfx::Rect(page_size)) {
130 canvas->scale(inverse_scale, inverse_scale);
131 SkRect sk_content_area = gfx::RectToSkRect(content_area);
132 canvas->clipRect(sk_content_area);
133 canvas->translate(sk_content_area.x(), sk_content_area.y());
134 canvas->scale(scale_factor, scale_factor);
135 }
136
halcanary8dd6f0242016-06-13 19:40:23137 data_->size_ = gfx::SizeFToSkSize(gfx::SizeF(page_size));
halcanary223f70a2016-06-09 00:07:42138 data_->scale_factor_ = scale_factor;
halcanary5baa8fb42015-04-03 21:39:30139 // We scale the recording canvas's size so that
140 // canvas->getTotalMatrix() returns a value that ignores the scale
halcanary223f70a2016-06-09 00:07:42141 // factor. We store the scale factor and re-apply it later.
142 // http://crbug.com/469656
halcanary5baa8fb42015-04-03 21:39:30143 // Recording canvas is owned by the data_->recorder_. No ref() necessary.
[email protected]8f879292011-04-08 00:21:20144}
145
tomhudsoned673beb2016-01-28 14:14:22146SkCanvas* PdfMetafileSkia::GetVectorCanvasForNewPage(
halcanary5be808e2014-11-10 22:20:05147 const gfx::Size& page_size,
148 const gfx::Rect& content_area,
149 const float& scale_factor) {
halcanary223f70a2016-06-09 00:07:42150 StartPage(page_size, content_area, scale_factor);
halcanaryff214762015-01-22 20:34:32151 return data_->recorder_.getRecordingCanvas();
[email protected]8f879292011-04-08 00:21:20152}
153
154bool PdfMetafileSkia::FinishPage() {
halcanaryff214762015-01-22 20:34:32155 if (!data_->recorder_.getRecordingCanvas())
156 return false;
halcanary223f70a2016-06-09 00:07:42157
158 sk_sp<SkPicture> pic = data_->recorder_.finishRecordingAsPicture();
159 if (data_->scale_factor_ != 1.0f) {
halcanary223f70a2016-06-09 00:07:42160 SkCanvas* canvas =
halcanary8dd6f0242016-06-13 19:40:23161 data_->recorder_.beginRecording(data_->size_.width(),
162 data_->size_.height());
halcanary223f70a2016-06-09 00:07:42163 canvas->scale(data_->scale_factor_, data_->scale_factor_);
164 canvas->drawPicture(pic);
165 pic = data_->recorder_.finishRecordingAsPicture();
166 }
halcanary8dd6f0242016-06-13 19:40:23167 data_->pages_.emplace_back(data_->size_, std::move(pic));
[email protected]8f879292011-04-08 00:21:20168 return true;
169}
170
171bool PdfMetafileSkia::FinishDocument() {
halcanaryff214762015-01-22 20:34:32172 // If we've already set the data in InitFromData, leave it be.
173 if (data_->pdf_data_)
174 return false;
[email protected]8f879292011-04-08 00:21:20175
halcanaryff214762015-01-22 20:34:32176 if (data_->recorder_.getRecordingCanvas())
thestig5ff7db22016-02-13 04:42:45177 FinishPage();
[email protected]67e16b392011-05-30 20:58:09178
halcanary223f70a2016-06-09 00:07:42179 SkDynamicMemoryWStream stream;
thestig192677ec2016-06-09 07:43:17180 // TODO(halcanary): support more document types (XPS, a sequence of display
181 // lists).
halcanary223f70a2016-06-09 00:07:42182 sk_sp<SkDocument> doc = MakePdfDocument(&stream);
halcanaryfb232282016-04-28 18:34:24183
halcanary8dd6f0242016-06-13 19:40:23184 for (const Page& page : data_->pages_) {
185 SkCanvas* canvas = doc->beginPage(page.size_.width(), page.size_.height());
186 canvas->drawPicture(page.content_);
halcanary223f70a2016-06-09 00:07:42187 doc->endPage();
halcanary0aeeb3332016-03-18 14:32:39188 }
halcanary223f70a2016-06-09 00:07:42189 if (!doc->close())
halcanaryff214762015-01-22 20:34:32190 return false;
[email protected]67e16b392011-05-30 20:58:09191
halcanary223f70a2016-06-09 00:07:42192 data_->pdf_data_.reset(stream.detachAsStream());
halcanaryff214762015-01-22 20:34:32193 return true;
[email protected]8f879292011-04-08 00:21:20194}
195
thestig707a24b22015-09-14 18:16:33196uint32_t PdfMetafileSkia::GetDataSize() const {
halcanaryff214762015-01-22 20:34:32197 if (!data_->pdf_data_)
198 return 0;
thestig707a24b22015-09-14 18:16:33199 return base::checked_cast<uint32_t>(data_->pdf_data_->getLength());
[email protected]8f879292011-04-08 00:21:20200}
201
202bool PdfMetafileSkia::GetData(void* dst_buffer,
thestig707a24b22015-09-14 18:16:33203 uint32_t dst_buffer_size) const {
halcanaryff214762015-01-22 20:34:32204 if (!data_->pdf_data_)
[email protected]8f879292011-04-08 00:21:20205 return false;
halcanaryff214762015-01-22 20:34:32206 return WriteAssetToBuffer(data_->pdf_data_.get(), dst_buffer,
207 base::checked_cast<size_t>(dst_buffer_size));
[email protected]8f879292011-04-08 00:21:20208}
209
[email protected]8f879292011-04-08 00:21:20210gfx::Rect PdfMetafileSkia::GetPageBounds(unsigned int page_number) const {
halcanaryff214762015-01-22 20:34:32211 if (page_number < data_->pages_.size()) {
halcanary8dd6f0242016-06-13 19:40:23212 SkSize size = data_->pages_[page_number].size_;
213 return gfx::Rect(gfx::ToRoundedInt(size.width()),
214 gfx::ToRoundedInt(size.height()));
halcanaryff214762015-01-22 20:34:32215 }
[email protected]8f879292011-04-08 00:21:20216 return gfx::Rect();
217}
218
219unsigned int PdfMetafileSkia::GetPageCount() const {
halcanaryff214762015-01-22 20:34:32220 return base::checked_cast<unsigned int>(data_->pages_.size());
[email protected]8f879292011-04-08 00:21:20221}
222
223gfx::NativeDrawingContext PdfMetafileSkia::context() const {
224 NOTREACHED();
thestig192677ec2016-06-09 07:43:17225 return nullptr;
[email protected]8f879292011-04-08 00:21:20226}
227
thestig192677ec2016-06-09 07:43:17228
[email protected]8f879292011-04-08 00:21:20229#if defined(OS_WIN)
230bool PdfMetafileSkia::Playback(gfx::NativeDrawingContext hdc,
231 const RECT* rect) const {
232 NOTREACHED();
233 return false;
234}
235
236bool PdfMetafileSkia::SafePlayback(gfx::NativeDrawingContext hdc) const {
237 NOTREACHED();
238 return false;
239}
240
[email protected]b8d85bc2011-06-22 13:34:57241#elif defined(OS_MACOSX)
242/* TODO(caryclark): The set up of PluginInstance::PrintPDFOutput may result in
243 rasterized output. Even if that flow uses PdfMetafileCg::RenderPage,
244 the drawing of the PDF into the canvas may result in a rasterized output.
245 PDFMetafileSkia::RenderPage should be not implemented as shown and instead
246 should do something like the following CL in PluginInstance::PrintPDFOutput:
247http://codereview.chromium.org/7200040/diff/1/webkit/plugins/ppapi/ppapi_plugin_instance.cc
248*/
249bool PdfMetafileSkia::RenderPage(unsigned int page_number,
250 CGContextRef context,
251 const CGRect rect,
[email protected]b5cf844c2012-06-18 21:49:20252 const MacRenderPageParams& params) const {
halcanaryff214762015-01-22 20:34:32253 DCHECK_GT(GetDataSize(), 0U);
[email protected]e195fbf52011-06-27 16:51:20254 if (data_->pdf_cg_.GetDataSize() == 0) {
halcanaryff214762015-01-22 20:34:32255 if (GetDataSize() == 0)
256 return false;
257 size_t length = data_->pdf_data_->getLength();
258 std::vector<uint8_t> buffer(length);
259 (void)WriteAssetToBuffer(data_->pdf_data_.get(), &buffer[0], length);
thestig707a24b22015-09-14 18:16:33260 data_->pdf_cg_.InitFromData(&buffer[0],
261 base::checked_cast<uint32_t>(length));
[email protected]e195fbf52011-06-27 16:51:20262 }
[email protected]b5cf844c2012-06-18 21:49:20263 return data_->pdf_cg_.RenderPage(page_number, context, rect, params);
[email protected]b8d85bc2011-06-22 13:34:57264}
265#endif
[email protected]8f879292011-04-08 00:21:20266
halcanary5be808e2014-11-10 22:20:05267bool PdfMetafileSkia::SaveTo(base::File* file) const {
268 if (GetDataSize() == 0U)
269 return false;
halcanaryff214762015-01-22 20:34:32270
271 // Calling duplicate() keeps original asset state unchanged.
dchengc3df9ba2016-04-07 23:09:32272 std::unique_ptr<SkStreamAsset> asset(data_->pdf_data_->duplicate());
halcanaryff214762015-01-22 20:34:32273
thestig192677ec2016-06-09 07:43:17274 const size_t kMaximumBufferSize = 1024 * 1024;
275 std::vector<char> buffer(std::min(kMaximumBufferSize, asset->getLength()));
halcanaryff214762015-01-22 20:34:32276 do {
277 size_t read_size = asset->read(&buffer[0], buffer.size());
278 if (read_size == 0)
279 break;
280 DCHECK_GE(buffer.size(), read_size);
281 if (!file->WriteAtCurrentPos(&buffer[0],
282 base::checked_cast<int>(read_size))) {
283 return false;
284 }
285 } while (!asset->isAtEnd());
286
287 return true;
halcanary5be808e2014-11-10 22:20:05288}
289
halcanary5be808e2014-11-10 22:20:05290PdfMetafileSkia::PdfMetafileSkia() : data_(new PdfMetafileSkiaData) {
[email protected]19b9d3b2011-07-23 02:08:57291}
[email protected]597516372011-07-01 05:10:44292
dchengc3df9ba2016-04-07 23:09:32293std::unique_ptr<PdfMetafileSkia> PdfMetafileSkia::GetMetafileForCurrentPage() {
halcanaryff214762015-01-22 20:34:32294 // If we only ever need the metafile for the last page, should we
295 // only keep a handle on one SkPicture?
dchengc3df9ba2016-04-07 23:09:32296 std::unique_ptr<PdfMetafileSkia> metafile(new PdfMetafileSkia);
halcanaryff214762015-01-22 20:34:32297
298 if (data_->pages_.size() == 0)
dchenge48600452015-12-28 02:24:50299 return metafile;
[email protected]597516372011-07-01 05:10:44300
halcanaryff214762015-01-22 20:34:32301 if (data_->recorder_.getRecordingCanvas()) // page outstanding
dchenge48600452015-12-28 02:24:50302 return metafile;
[email protected]597516372011-07-01 05:10:44303
halcanary223f70a2016-06-09 00:07:42304 metafile->data_->pages_.push_back(data_->pages_.back());
halcanaryff214762015-01-22 20:34:32305
306 if (!metafile->FinishDocument()) // Generate PDF.
vitalybuka5d1290582014-09-12 09:19:59307 metafile.reset();
halcanaryff214762015-01-22 20:34:32308
dchenge48600452015-12-28 02:24:50309 return metafile;
[email protected]597516372011-07-01 05:10:44310}
[email protected]8f879292011-04-08 00:21:20311
[email protected]8f879292011-04-08 00:21:20312} // namespace printing