blob: 29f78b6d4c717e8540a283b5c554549793125def [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
70struct PdfMetafileSkiaData {
halcanaryff214762015-01-22 20:34:3271 SkPictureRecorder recorder_; // Current recording
72
halcanary223f70a2016-06-09 00:07:4273 std::vector<sk_sp<SkPicture>> pages_;
dchengc3df9ba2016-04-07 23:09:3274 std::unique_ptr<SkStreamAsset> pdf_data_;
halcanaryff214762015-01-22 20:34:3275
halcanary223f70a2016-06-09 00:07:4276 // The scale factor is used because Blink occasionally calls
77 // SkCanvas::getTotalMatrix() even though the total matrix is not as
78 // meaningful for a vector canvas as for a raster canvas.
79 float scale_factor_;
80
[email protected]b8d85bc2011-06-22 13:34:5781#if defined(OS_MACOSX)
82 PdfMetafileCg pdf_cg_;
83#endif
[email protected]8f879292011-04-08 00:21:2084};
85
86PdfMetafileSkia::~PdfMetafileSkia() {}
87
88bool PdfMetafileSkia::Init() {
89 return true;
90}
halcanaryff214762015-01-22 20:34:3291
92// TODO(halcanary): Create a Metafile class that only stores data.
93// Metafile::InitFromData is orthogonal to what the rest of
94// PdfMetafileSkia does.
[email protected]8f879292011-04-08 00:21:2095bool PdfMetafileSkia::InitFromData(const void* src_buffer,
thestig707a24b22015-09-14 18:16:3396 uint32_t src_buffer_size) {
halcanaryff214762015-01-22 20:34:3297 data_->pdf_data_.reset(new SkMemoryStream(src_buffer, src_buffer_size, true));
98 return true;
[email protected]8f879292011-04-08 00:21:2099}
100
thestig192677ec2016-06-09 07:43:17101void PdfMetafileSkia::StartPage(const gfx::Size& page_size,
halcanary5be808e2014-11-10 22:20:05102 const gfx::Rect& content_area,
103 const float& scale_factor) {
halcanary223f70a2016-06-09 00:07:42104 DCHECK_GT(page_size.width(), 0);
105 DCHECK_GT(page_size.height(), 0);
106 DCHECK_GT(scale_factor, 0.0f);
halcanaryff214762015-01-22 20:34:32107 if (data_->recorder_.getRecordingCanvas())
thestig5ff7db22016-02-13 04:42:45108 FinishPage();
halcanaryff214762015-01-22 20:34:32109 DCHECK(!data_->recorder_.getRecordingCanvas());
halcanary223f70a2016-06-09 00:07:42110
111 float inverse_scale = 1.0 / scale_factor;
112 SkCanvas* canvas = data_->recorder_.beginRecording(
113 inverse_scale * page_size.width(), inverse_scale * page_size.height());
114 if (content_area != gfx::Rect(page_size)) {
115 canvas->scale(inverse_scale, inverse_scale);
116 SkRect sk_content_area = gfx::RectToSkRect(content_area);
117 canvas->clipRect(sk_content_area);
118 canvas->translate(sk_content_area.x(), sk_content_area.y());
119 canvas->scale(scale_factor, scale_factor);
120 }
121
122 data_->scale_factor_ = scale_factor;
halcanary5baa8fb42015-04-03 21:39:30123 // We scale the recording canvas's size so that
124 // canvas->getTotalMatrix() returns a value that ignores the scale
halcanary223f70a2016-06-09 00:07:42125 // factor. We store the scale factor and re-apply it later.
126 // http://crbug.com/469656
halcanary5baa8fb42015-04-03 21:39:30127 // Recording canvas is owned by the data_->recorder_. No ref() necessary.
[email protected]8f879292011-04-08 00:21:20128}
129
tomhudsoned673beb2016-01-28 14:14:22130SkCanvas* PdfMetafileSkia::GetVectorCanvasForNewPage(
halcanary5be808e2014-11-10 22:20:05131 const gfx::Size& page_size,
132 const gfx::Rect& content_area,
133 const float& scale_factor) {
halcanary223f70a2016-06-09 00:07:42134 StartPage(page_size, content_area, scale_factor);
halcanaryff214762015-01-22 20:34:32135 return data_->recorder_.getRecordingCanvas();
[email protected]8f879292011-04-08 00:21:20136}
137
138bool PdfMetafileSkia::FinishPage() {
halcanaryff214762015-01-22 20:34:32139 if (!data_->recorder_.getRecordingCanvas())
140 return false;
halcanary223f70a2016-06-09 00:07:42141
142 sk_sp<SkPicture> pic = data_->recorder_.finishRecordingAsPicture();
143 if (data_->scale_factor_ != 1.0f) {
144 SkRect rect = pic->cullRect();
145 DCHECK_EQ(rect.x(), 0);
146 DCHECK_EQ(rect.y(), 0);
147 DCHECK_GT(rect.right(), 0);
148 DCHECK_GT(rect.bottom(), 0);
149 SkCanvas* canvas =
150 data_->recorder_.beginRecording(data_->scale_factor_ * rect.right(),
151 data_->scale_factor_ * rect.bottom());
152 canvas->scale(data_->scale_factor_, data_->scale_factor_);
153 canvas->drawPicture(pic);
154 pic = data_->recorder_.finishRecordingAsPicture();
155 }
156 data_->pages_.push_back(std::move(pic));
[email protected]8f879292011-04-08 00:21:20157 return true;
158}
159
160bool PdfMetafileSkia::FinishDocument() {
halcanaryff214762015-01-22 20:34:32161 // If we've already set the data in InitFromData, leave it be.
162 if (data_->pdf_data_)
163 return false;
[email protected]8f879292011-04-08 00:21:20164
halcanaryff214762015-01-22 20:34:32165 if (data_->recorder_.getRecordingCanvas())
thestig5ff7db22016-02-13 04:42:45166 FinishPage();
[email protected]67e16b392011-05-30 20:58:09167
halcanary223f70a2016-06-09 00:07:42168 SkDynamicMemoryWStream stream;
thestig192677ec2016-06-09 07:43:17169 // TODO(halcanary): support more document types (XPS, a sequence of display
170 // lists).
halcanary223f70a2016-06-09 00:07:42171 sk_sp<SkDocument> doc = MakePdfDocument(&stream);
halcanaryfb232282016-04-28 18:34:24172
halcanary223f70a2016-06-09 00:07:42173 for (const sk_sp<SkPicture>& page : data_->pages_) {
174 SkRect rect = page->cullRect();
175 DCHECK_EQ(rect.x(), 0);
176 DCHECK_EQ(rect.y(), 0);
177 DCHECK_GT(rect.right(), 0);
178 DCHECK_GT(rect.bottom(), 0);
179 SkCanvas* canvas = doc->beginPage(rect.right(), rect.bottom());
halcanary223f70a2016-06-09 00:07:42180 canvas->drawPicture(page);
181 doc->endPage();
halcanary0aeeb3332016-03-18 14:32:39182 }
halcanary223f70a2016-06-09 00:07:42183 if (!doc->close())
halcanaryff214762015-01-22 20:34:32184 return false;
[email protected]67e16b392011-05-30 20:58:09185
halcanary223f70a2016-06-09 00:07:42186 data_->pdf_data_.reset(stream.detachAsStream());
halcanaryff214762015-01-22 20:34:32187 return true;
[email protected]8f879292011-04-08 00:21:20188}
189
thestig707a24b22015-09-14 18:16:33190uint32_t PdfMetafileSkia::GetDataSize() const {
halcanaryff214762015-01-22 20:34:32191 if (!data_->pdf_data_)
192 return 0;
thestig707a24b22015-09-14 18:16:33193 return base::checked_cast<uint32_t>(data_->pdf_data_->getLength());
[email protected]8f879292011-04-08 00:21:20194}
195
196bool PdfMetafileSkia::GetData(void* dst_buffer,
thestig707a24b22015-09-14 18:16:33197 uint32_t dst_buffer_size) const {
halcanaryff214762015-01-22 20:34:32198 if (!data_->pdf_data_)
[email protected]8f879292011-04-08 00:21:20199 return false;
halcanaryff214762015-01-22 20:34:32200 return WriteAssetToBuffer(data_->pdf_data_.get(), dst_buffer,
201 base::checked_cast<size_t>(dst_buffer_size));
[email protected]8f879292011-04-08 00:21:20202}
203
[email protected]8f879292011-04-08 00:21:20204gfx::Rect PdfMetafileSkia::GetPageBounds(unsigned int page_number) const {
halcanaryff214762015-01-22 20:34:32205 if (page_number < data_->pages_.size()) {
halcanary223f70a2016-06-09 00:07:42206 const sk_sp<SkPicture>& page = data_->pages_[page_number];
207 SkRect rect = page->cullRect();
208 DCHECK_EQ(rect.x(), 0);
209 DCHECK_EQ(rect.y(), 0);
210 DCHECK_GT(rect.right(), 0);
211 DCHECK_GT(rect.bottom(), 0);
212 return gfx::Rect(gfx::ToRoundedInt(rect.right()),
213 gfx::ToRoundedInt(rect.bottom()));
halcanaryff214762015-01-22 20:34:32214 }
[email protected]8f879292011-04-08 00:21:20215 return gfx::Rect();
216}
217
218unsigned int PdfMetafileSkia::GetPageCount() const {
halcanaryff214762015-01-22 20:34:32219 return base::checked_cast<unsigned int>(data_->pages_.size());
[email protected]8f879292011-04-08 00:21:20220}
221
222gfx::NativeDrawingContext PdfMetafileSkia::context() const {
223 NOTREACHED();
thestig192677ec2016-06-09 07:43:17224 return nullptr;
[email protected]8f879292011-04-08 00:21:20225}
226
thestig192677ec2016-06-09 07:43:17227
[email protected]8f879292011-04-08 00:21:20228#if defined(OS_WIN)
229bool PdfMetafileSkia::Playback(gfx::NativeDrawingContext hdc,
230 const RECT* rect) const {
231 NOTREACHED();
232 return false;
233}
234
235bool PdfMetafileSkia::SafePlayback(gfx::NativeDrawingContext hdc) const {
236 NOTREACHED();
237 return false;
238}
239
[email protected]b8d85bc2011-06-22 13:34:57240#elif defined(OS_MACOSX)
241/* TODO(caryclark): The set up of PluginInstance::PrintPDFOutput may result in
242 rasterized output. Even if that flow uses PdfMetafileCg::RenderPage,
243 the drawing of the PDF into the canvas may result in a rasterized output.
244 PDFMetafileSkia::RenderPage should be not implemented as shown and instead
245 should do something like the following CL in PluginInstance::PrintPDFOutput:
246http://codereview.chromium.org/7200040/diff/1/webkit/plugins/ppapi/ppapi_plugin_instance.cc
247*/
248bool PdfMetafileSkia::RenderPage(unsigned int page_number,
249 CGContextRef context,
250 const CGRect rect,
[email protected]b5cf844c2012-06-18 21:49:20251 const MacRenderPageParams& params) const {
halcanaryff214762015-01-22 20:34:32252 DCHECK_GT(GetDataSize(), 0U);
[email protected]e195fbf52011-06-27 16:51:20253 if (data_->pdf_cg_.GetDataSize() == 0) {
halcanaryff214762015-01-22 20:34:32254 if (GetDataSize() == 0)
255 return false;
256 size_t length = data_->pdf_data_->getLength();
257 std::vector<uint8_t> buffer(length);
258 (void)WriteAssetToBuffer(data_->pdf_data_.get(), &buffer[0], length);
thestig707a24b22015-09-14 18:16:33259 data_->pdf_cg_.InitFromData(&buffer[0],
260 base::checked_cast<uint32_t>(length));
[email protected]e195fbf52011-06-27 16:51:20261 }
[email protected]b5cf844c2012-06-18 21:49:20262 return data_->pdf_cg_.RenderPage(page_number, context, rect, params);
[email protected]b8d85bc2011-06-22 13:34:57263}
264#endif
[email protected]8f879292011-04-08 00:21:20265
halcanary5be808e2014-11-10 22:20:05266bool PdfMetafileSkia::SaveTo(base::File* file) const {
267 if (GetDataSize() == 0U)
268 return false;
halcanaryff214762015-01-22 20:34:32269
270 // Calling duplicate() keeps original asset state unchanged.
dchengc3df9ba2016-04-07 23:09:32271 std::unique_ptr<SkStreamAsset> asset(data_->pdf_data_->duplicate());
halcanaryff214762015-01-22 20:34:32272
thestig192677ec2016-06-09 07:43:17273 const size_t kMaximumBufferSize = 1024 * 1024;
274 std::vector<char> buffer(std::min(kMaximumBufferSize, asset->getLength()));
halcanaryff214762015-01-22 20:34:32275 do {
276 size_t read_size = asset->read(&buffer[0], buffer.size());
277 if (read_size == 0)
278 break;
279 DCHECK_GE(buffer.size(), read_size);
280 if (!file->WriteAtCurrentPos(&buffer[0],
281 base::checked_cast<int>(read_size))) {
282 return false;
283 }
284 } while (!asset->isAtEnd());
285
286 return true;
halcanary5be808e2014-11-10 22:20:05287}
288
halcanary5be808e2014-11-10 22:20:05289PdfMetafileSkia::PdfMetafileSkia() : data_(new PdfMetafileSkiaData) {
[email protected]19b9d3b2011-07-23 02:08:57290}
[email protected]597516372011-07-01 05:10:44291
dchengc3df9ba2016-04-07 23:09:32292std::unique_ptr<PdfMetafileSkia> PdfMetafileSkia::GetMetafileForCurrentPage() {
halcanaryff214762015-01-22 20:34:32293 // If we only ever need the metafile for the last page, should we
294 // only keep a handle on one SkPicture?
dchengc3df9ba2016-04-07 23:09:32295 std::unique_ptr<PdfMetafileSkia> metafile(new PdfMetafileSkia);
halcanaryff214762015-01-22 20:34:32296
297 if (data_->pages_.size() == 0)
dchenge48600452015-12-28 02:24:50298 return metafile;
[email protected]597516372011-07-01 05:10:44299
halcanaryff214762015-01-22 20:34:32300 if (data_->recorder_.getRecordingCanvas()) // page outstanding
dchenge48600452015-12-28 02:24:50301 return metafile;
[email protected]597516372011-07-01 05:10:44302
halcanary223f70a2016-06-09 00:07:42303 metafile->data_->pages_.push_back(data_->pages_.back());
halcanaryff214762015-01-22 20:34:32304
305 if (!metafile->FinishDocument()) // Generate PDF.
vitalybuka5d1290582014-09-12 09:19:59306 metafile.reset();
halcanaryff214762015-01-22 20:34:32307
dchenge48600452015-12-28 02:24:50308 return metafile;
[email protected]597516372011-07-01 05:10:44309}
[email protected]8f879292011-04-08 00:21:20310
[email protected]8f879292011-04-08 00:21:20311} // namespace printing