blob: dd9149483fad62f8ab47a18d59c8b46c9ec9ac8f [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
thestig6b4461f2016-10-28 19:34:307#include <algorithm>
8#include <string>
9#include <utility>
10#include <vector>
11
halcanary223f70a2016-06-09 00:07:4212#include "base/files/file.h"
thestig6b4461f2016-10-28 19:34:3013#include "base/memory/ptr_util.h"
halcanary67ce00b82015-09-27 21:59:5314#include "base/time/time.h"
warxafc38d52017-04-26 22:59:5015#include "cc/paint/paint_canvas.h"
enne7b64edf32017-02-16 20:10:0216#include "cc/paint/paint_record.h"
17#include "cc/paint/paint_recorder.h"
halcanary73b63fd2015-11-06 00:02:1418#include "printing/print_settings.h"
halcanaryff214762015-01-22 20:34:3219#include "third_party/skia/include/core/SkDocument.h"
[email protected]8f879292011-04-08 00:21:2020#include "third_party/skia/include/core/SkStream.h"
halcanaryffa005b2016-06-15 17:13:1621// Note that headers in third_party/skia/src are fragile. This is
22// an experimental, fragile, and diagnostic-only document type.
23#include "third_party/skia/src/utils/SkMultiPictureDocument.h"
halcanary223f70a2016-06-09 00:07:4224#include "ui/gfx/geometry/safe_integer_conversions.h"
halcanaryff214762015-01-22 20:34:3225#include "ui/gfx/skia_util.h"
[email protected]8f879292011-04-08 00:21:2026
thestig99fc2132017-04-27 03:37:5427#if defined(OS_MACOSX)
28#include "printing/pdf_metafile_cg_mac.h"
29#endif
30
31#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
thestige3f5f5d42015-07-16 19:46:2437bool WriteAssetToBuffer(const SkStreamAsset* asset,
38 void* buffer,
39 size_t size) {
halcanaryff214762015-01-22 20:34:3240 // Calling duplicate() keeps original asset state unchanged.
dchengc3df9ba2016-04-07 23:09:3241 std::unique_ptr<SkStreamAsset> assetCopy(asset->duplicate());
halcanaryff214762015-01-22 20:34:3242 size_t length = assetCopy->getLength();
43 if (length > size)
44 return false;
45 return (length == assetCopy->read(buffer, length));
46}
47
thestig5ff7db22016-02-13 04:42:4548SkTime::DateTime TimeToSkTime(base::Time time) {
halcanary223f70a2016-06-09 00:07:4249 base::Time::Exploded exploded;
50 time.UTCExplode(&exploded);
51 SkTime::DateTime skdate;
52 skdate.fTimeZoneMinutes = 0;
53 skdate.fYear = exploded.year;
54 skdate.fMonth = exploded.month;
55 skdate.fDayOfWeek = exploded.day_of_week;
56 skdate.fDay = exploded.day_of_month;
57 skdate.fHour = exploded.hour;
58 skdate.fMinute = exploded.minute;
59 skdate.fSecond = exploded.second;
60 return skdate;
61}
62
63sk_sp<SkDocument> MakePdfDocument(SkWStream* wStream) {
64 SkDocument::PDFMetadata metadata;
65 SkTime::DateTime now = TimeToSkTime(base::Time::Now());
66 metadata.fCreation.fEnabled = true;
67 metadata.fCreation.fDateTime = now;
68 metadata.fModified.fEnabled = true;
69 metadata.fModified.fDateTime = now;
70 const std::string& agent = printing::GetAgent();
71 metadata.fCreator = agent.empty() ? SkString("Chromium")
72 : SkString(agent.c_str(), agent.size());
73 return SkDocument::MakePDF(wStream, SK_ScalarDefaultRasterDPI, metadata,
74 nullptr, false);
thestig5ff7db22016-02-13 04:42:4575}
76
thestige3f5f5d42015-07-16 19:46:2477} // namespace
78
[email protected]8f879292011-04-08 00:21:2079namespace printing {
80
halcanary8dd6f0242016-06-13 19:40:2381struct Page {
enne7b64edf32017-02-16 20:10:0282 Page(SkSize s, sk_sp<cc::PaintRecord> c) : size_(s), content_(std::move(c)) {}
halcanary8dd6f0242016-06-13 19:40:2383 Page(Page&& that) : size_(that.size_), content_(std::move(that.content_)) {}
84 Page(const Page&) = default;
85 Page& operator=(const Page&) = default;
86 Page& operator=(Page&& that) {
87 size_ = that.size_;
88 content_ = std::move(that.content_);
89 return *this;
90 }
91 SkSize size_;
enne7b64edf32017-02-16 20:10:0292 sk_sp<cc::PaintRecord> content_;
halcanary8dd6f0242016-06-13 19:40:2393};
94
[email protected]8f879292011-04-08 00:21:2095struct PdfMetafileSkiaData {
enne7b64edf32017-02-16 20:10:0296 cc::PaintRecorder recorder_; // Current recording
halcanaryff214762015-01-22 20:34:3297
halcanary8dd6f0242016-06-13 19:40:2398 std::vector<Page> pages_;
dchengc3df9ba2016-04-07 23:09:3299 std::unique_ptr<SkStreamAsset> pdf_data_;
halcanaryff214762015-01-22 20:34:32100
halcanary223f70a2016-06-09 00:07:42101 // The scale factor is used because Blink occasionally calls
enne7b64edf32017-02-16 20:10:02102 // PaintCanvas::getTotalMatrix() even though the total matrix is not as
halcanary223f70a2016-06-09 00:07:42103 // meaningful for a vector canvas as for a raster canvas.
104 float scale_factor_;
halcanary8dd6f0242016-06-13 19:40:23105 SkSize size_;
halcanaryffa005b2016-06-15 17:13:16106 SkiaDocumentType type_;
thestig99fc2132017-04-27 03:37:54107
108#if defined(OS_MACOSX)
109 PdfMetafileCg pdf_cg_;
110#endif
[email protected]8f879292011-04-08 00:21:20111};
112
113PdfMetafileSkia::~PdfMetafileSkia() {}
114
115bool PdfMetafileSkia::Init() {
116 return true;
117}
halcanaryff214762015-01-22 20:34:32118
119// TODO(halcanary): Create a Metafile class that only stores data.
120// Metafile::InitFromData is orthogonal to what the rest of
121// PdfMetafileSkia does.
[email protected]8f879292011-04-08 00:21:20122bool PdfMetafileSkia::InitFromData(const void* src_buffer,
thestig6b4461f2016-10-28 19:34:30123 size_t src_buffer_size) {
124 data_->pdf_data_ = base::MakeUnique<SkMemoryStream>(
125 src_buffer, src_buffer_size, true /* copy_data? */);
halcanaryff214762015-01-22 20:34:32126 return true;
[email protected]8f879292011-04-08 00:21:20127}
128
thestig192677ec2016-06-09 07:43:17129void PdfMetafileSkia::StartPage(const gfx::Size& page_size,
halcanary5be808e2014-11-10 22:20:05130 const gfx::Rect& content_area,
131 const float& scale_factor) {
halcanary223f70a2016-06-09 00:07:42132 DCHECK_GT(page_size.width(), 0);
133 DCHECK_GT(page_size.height(), 0);
134 DCHECK_GT(scale_factor, 0.0f);
halcanaryff214762015-01-22 20:34:32135 if (data_->recorder_.getRecordingCanvas())
thestig5ff7db22016-02-13 04:42:45136 FinishPage();
halcanaryff214762015-01-22 20:34:32137 DCHECK(!data_->recorder_.getRecordingCanvas());
halcanary223f70a2016-06-09 00:07:42138
139 float inverse_scale = 1.0 / scale_factor;
enne7b64edf32017-02-16 20:10:02140 cc::PaintCanvas* canvas = data_->recorder_.beginRecording(
halcanary223f70a2016-06-09 00:07:42141 inverse_scale * page_size.width(), inverse_scale * page_size.height());
halcanaryffa005b2016-06-15 17:13:16142 // Recording canvas is owned by the data_->recorder_. No ref() necessary.
halcanary223f70a2016-06-09 00:07:42143 if (content_area != gfx::Rect(page_size)) {
144 canvas->scale(inverse_scale, inverse_scale);
145 SkRect sk_content_area = gfx::RectToSkRect(content_area);
146 canvas->clipRect(sk_content_area);
147 canvas->translate(sk_content_area.x(), sk_content_area.y());
148 canvas->scale(scale_factor, scale_factor);
149 }
150
halcanary8dd6f0242016-06-13 19:40:23151 data_->size_ = gfx::SizeFToSkSize(gfx::SizeF(page_size));
halcanary223f70a2016-06-09 00:07:42152 data_->scale_factor_ = scale_factor;
halcanary5baa8fb42015-04-03 21:39:30153 // We scale the recording canvas's size so that
154 // canvas->getTotalMatrix() returns a value that ignores the scale
halcanary223f70a2016-06-09 00:07:42155 // factor. We store the scale factor and re-apply it later.
156 // http://crbug.com/469656
[email protected]8f879292011-04-08 00:21:20157}
158
enne7b64edf32017-02-16 20:10:02159cc::PaintCanvas* PdfMetafileSkia::GetVectorCanvasForNewPage(
halcanary5be808e2014-11-10 22:20:05160 const gfx::Size& page_size,
161 const gfx::Rect& content_area,
162 const float& scale_factor) {
halcanary223f70a2016-06-09 00:07:42163 StartPage(page_size, content_area, scale_factor);
halcanaryff214762015-01-22 20:34:32164 return data_->recorder_.getRecordingCanvas();
[email protected]8f879292011-04-08 00:21:20165}
166
167bool PdfMetafileSkia::FinishPage() {
halcanaryff214762015-01-22 20:34:32168 if (!data_->recorder_.getRecordingCanvas())
169 return false;
halcanary223f70a2016-06-09 00:07:42170
enne7b64edf32017-02-16 20:10:02171 sk_sp<cc::PaintRecord> pic = data_->recorder_.finishRecordingAsPicture();
halcanary223f70a2016-06-09 00:07:42172 if (data_->scale_factor_ != 1.0f) {
enne7b64edf32017-02-16 20:10:02173 cc::PaintCanvas* canvas = data_->recorder_.beginRecording(
174 data_->size_.width(), data_->size_.height());
halcanary223f70a2016-06-09 00:07:42175 canvas->scale(data_->scale_factor_, data_->scale_factor_);
176 canvas->drawPicture(pic);
177 pic = data_->recorder_.finishRecordingAsPicture();
178 }
halcanary8dd6f0242016-06-13 19:40:23179 data_->pages_.emplace_back(data_->size_, std::move(pic));
[email protected]8f879292011-04-08 00:21:20180 return true;
181}
182
183bool PdfMetafileSkia::FinishDocument() {
halcanaryff214762015-01-22 20:34:32184 // If we've already set the data in InitFromData, leave it be.
185 if (data_->pdf_data_)
186 return false;
[email protected]8f879292011-04-08 00:21:20187
halcanaryff214762015-01-22 20:34:32188 if (data_->recorder_.getRecordingCanvas())
thestig5ff7db22016-02-13 04:42:45189 FinishPage();
[email protected]67e16b392011-05-30 20:58:09190
halcanary223f70a2016-06-09 00:07:42191 SkDynamicMemoryWStream stream;
halcanaryffa005b2016-06-15 17:13:16192 sk_sp<SkDocument> doc;
193 switch (data_->type_) {
194 case PDF_SKIA_DOCUMENT_TYPE:
195 doc = MakePdfDocument(&stream);
196 break;
197 case MSKP_SKIA_DOCUMENT_TYPE:
198 doc = SkMakeMultiPictureDocument(&stream);
199 break;
200 }
halcanaryfb232282016-04-28 18:34:24201
halcanary8dd6f0242016-06-13 19:40:23202 for (const Page& page : data_->pages_) {
enne98c9f8052017-03-15 19:38:22203 cc::SkiaPaintCanvas canvas(
enne7b64edf32017-02-16 20:10:02204 doc->beginPage(page.size_.width(), page.size_.height()));
enned2501572017-03-09 19:59:17205 canvas.drawPicture(page.content_);
halcanary223f70a2016-06-09 00:07:42206 doc->endPage();
halcanary0aeeb3332016-03-18 14:32:39207 }
reede35e0532016-09-22 17:30:29208 doc->close();
[email protected]67e16b392011-05-30 20:58:09209
halcanaryde655aa2017-04-03 16:16:13210 data_->pdf_data_ = stream.detachAsStream();
halcanaryff214762015-01-22 20:34:32211 return true;
[email protected]8f879292011-04-08 00:21:20212}
213
thestig707a24b22015-09-14 18:16:33214uint32_t PdfMetafileSkia::GetDataSize() const {
halcanaryff214762015-01-22 20:34:32215 if (!data_->pdf_data_)
216 return 0;
thestig707a24b22015-09-14 18:16:33217 return base::checked_cast<uint32_t>(data_->pdf_data_->getLength());
[email protected]8f879292011-04-08 00:21:20218}
219
220bool PdfMetafileSkia::GetData(void* dst_buffer,
thestig707a24b22015-09-14 18:16:33221 uint32_t dst_buffer_size) const {
halcanaryff214762015-01-22 20:34:32222 if (!data_->pdf_data_)
[email protected]8f879292011-04-08 00:21:20223 return false;
halcanaryff214762015-01-22 20:34:32224 return WriteAssetToBuffer(data_->pdf_data_.get(), dst_buffer,
225 base::checked_cast<size_t>(dst_buffer_size));
[email protected]8f879292011-04-08 00:21:20226}
227
[email protected]8f879292011-04-08 00:21:20228gfx::Rect PdfMetafileSkia::GetPageBounds(unsigned int page_number) const {
halcanaryff214762015-01-22 20:34:32229 if (page_number < data_->pages_.size()) {
halcanary8dd6f0242016-06-13 19:40:23230 SkSize size = data_->pages_[page_number].size_;
231 return gfx::Rect(gfx::ToRoundedInt(size.width()),
232 gfx::ToRoundedInt(size.height()));
halcanaryff214762015-01-22 20:34:32233 }
[email protected]8f879292011-04-08 00:21:20234 return gfx::Rect();
235}
236
237unsigned int PdfMetafileSkia::GetPageCount() const {
halcanaryff214762015-01-22 20:34:32238 return base::checked_cast<unsigned int>(data_->pages_.size());
[email protected]8f879292011-04-08 00:21:20239}
240
tfarina876d1e02016-10-11 23:12:53241skia::NativeDrawingContext PdfMetafileSkia::context() const {
[email protected]8f879292011-04-08 00:21:20242 NOTREACHED();
thestig192677ec2016-06-09 07:43:17243 return nullptr;
[email protected]8f879292011-04-08 00:21:20244}
245
thestig192677ec2016-06-09 07:43:17246
[email protected]8f879292011-04-08 00:21:20247#if defined(OS_WIN)
thestig3109df12017-04-26 21:57:25248bool PdfMetafileSkia::Playback(skia::NativeDrawingContext hdc,
249 const RECT* rect) const {
250 NOTREACHED();
251 return false;
252}
253
tfarina876d1e02016-10-11 23:12:53254bool PdfMetafileSkia::SafePlayback(skia::NativeDrawingContext hdc) const {
[email protected]8f879292011-04-08 00:21:20255 NOTREACHED();
256 return false;
257}
thestig99fc2132017-04-27 03:37:54258
259#elif defined(OS_MACOSX)
260/* TODO(caryclark): The set up of PluginInstance::PrintPDFOutput may result in
261 rasterized output. Even if that flow uses PdfMetafileCg::RenderPage,
262 the drawing of the PDF into the canvas may result in a rasterized output.
263 PDFMetafileSkia::RenderPage should be not implemented as shown and instead
264 should do something like the following CL in PluginInstance::PrintPDFOutput:
265http://codereview.chromium.org/7200040/diff/1/webkit/plugins/ppapi/ppapi_plugin_instance.cc
266*/
267bool PdfMetafileSkia::RenderPage(unsigned int page_number,
268 CGContextRef context,
269 const CGRect rect,
270 const MacRenderPageParams& params) const {
271 DCHECK_GT(GetDataSize(), 0U);
272 if (data_->pdf_cg_.GetDataSize() == 0) {
273 if (GetDataSize() == 0)
274 return false;
275 size_t length = data_->pdf_data_->getLength();
276 std::vector<uint8_t> buffer(length);
277 (void)WriteAssetToBuffer(data_->pdf_data_.get(), &buffer[0], length);
278 data_->pdf_cg_.InitFromData(&buffer[0], length);
279 }
280 return data_->pdf_cg_.RenderPage(page_number, context, rect, params);
281}
[email protected]b8d85bc2011-06-22 13:34:57282#endif
[email protected]8f879292011-04-08 00:21:20283
halcanary5be808e2014-11-10 22:20:05284bool PdfMetafileSkia::SaveTo(base::File* file) const {
285 if (GetDataSize() == 0U)
286 return false;
halcanaryff214762015-01-22 20:34:32287
288 // Calling duplicate() keeps original asset state unchanged.
dchengc3df9ba2016-04-07 23:09:32289 std::unique_ptr<SkStreamAsset> asset(data_->pdf_data_->duplicate());
halcanaryff214762015-01-22 20:34:32290
thestig192677ec2016-06-09 07:43:17291 const size_t kMaximumBufferSize = 1024 * 1024;
292 std::vector<char> buffer(std::min(kMaximumBufferSize, asset->getLength()));
halcanaryff214762015-01-22 20:34:32293 do {
294 size_t read_size = asset->read(&buffer[0], buffer.size());
295 if (read_size == 0)
296 break;
297 DCHECK_GE(buffer.size(), read_size);
298 if (!file->WriteAtCurrentPos(&buffer[0],
299 base::checked_cast<int>(read_size))) {
300 return false;
301 }
302 } while (!asset->isAtEnd());
303
304 return true;
halcanary5be808e2014-11-10 22:20:05305}
306
halcanaryffa005b2016-06-15 17:13:16307PdfMetafileSkia::PdfMetafileSkia(SkiaDocumentType type)
308 : data_(new PdfMetafileSkiaData) {
309 data_->type_ = type;
[email protected]19b9d3b2011-07-23 02:08:57310}
[email protected]597516372011-07-01 05:10:44311
halcanaryffa005b2016-06-15 17:13:16312std::unique_ptr<PdfMetafileSkia> PdfMetafileSkia::GetMetafileForCurrentPage(
313 SkiaDocumentType type) {
halcanaryff214762015-01-22 20:34:32314 // If we only ever need the metafile for the last page, should we
enne7b64edf32017-02-16 20:10:02315 // only keep a handle on one PaintRecord?
halcanaryffa005b2016-06-15 17:13:16316 std::unique_ptr<PdfMetafileSkia> metafile(new PdfMetafileSkia(type));
halcanaryff214762015-01-22 20:34:32317
318 if (data_->pages_.size() == 0)
dchenge48600452015-12-28 02:24:50319 return metafile;
[email protected]597516372011-07-01 05:10:44320
halcanaryff214762015-01-22 20:34:32321 if (data_->recorder_.getRecordingCanvas()) // page outstanding
dchenge48600452015-12-28 02:24:50322 return metafile;
[email protected]597516372011-07-01 05:10:44323
halcanary223f70a2016-06-09 00:07:42324 metafile->data_->pages_.push_back(data_->pages_.back());
halcanaryff214762015-01-22 20:34:32325
326 if (!metafile->FinishDocument()) // Generate PDF.
vitalybuka5d1290582014-09-12 09:19:59327 metafile.reset();
halcanaryff214762015-01-22 20:34:32328
dchenge48600452015-12-28 02:24:50329 return metafile;
[email protected]597516372011-07-01 05:10:44330}
[email protected]8f879292011-04-08 00:21:20331
[email protected]8f879292011-04-08 00:21:20332} // namespace printing