blob: 12b24adec07c104c234741c369613838313070ae [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"
enne7b64edf32017-02-16 20:10:0215#include "cc/paint/paint_record.h"
16#include "cc/paint/paint_recorder.h"
ennef5a5ed82017-04-26 16:04:1217#include "cc/paint/skia_paint_canvas.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
halcanaryff214762015-01-22 20:34:3227namespace {
thestige3f5f5d42015-07-16 19:46:2428
thestige3f5f5d42015-07-16 19:46:2429bool WriteAssetToBuffer(const SkStreamAsset* asset,
30 void* buffer,
31 size_t size) {
halcanaryff214762015-01-22 20:34:3232 // Calling duplicate() keeps original asset state unchanged.
dchengc3df9ba2016-04-07 23:09:3233 std::unique_ptr<SkStreamAsset> assetCopy(asset->duplicate());
halcanaryff214762015-01-22 20:34:3234 size_t length = assetCopy->getLength();
35 if (length > size)
36 return false;
37 return (length == assetCopy->read(buffer, length));
38}
39
thestig5ff7db22016-02-13 04:42:4540SkTime::DateTime TimeToSkTime(base::Time time) {
halcanary223f70a2016-06-09 00:07:4241 base::Time::Exploded exploded;
42 time.UTCExplode(&exploded);
43 SkTime::DateTime skdate;
44 skdate.fTimeZoneMinutes = 0;
45 skdate.fYear = exploded.year;
46 skdate.fMonth = exploded.month;
47 skdate.fDayOfWeek = exploded.day_of_week;
48 skdate.fDay = exploded.day_of_month;
49 skdate.fHour = exploded.hour;
50 skdate.fMinute = exploded.minute;
51 skdate.fSecond = exploded.second;
52 return skdate;
53}
54
55sk_sp<SkDocument> MakePdfDocument(SkWStream* wStream) {
56 SkDocument::PDFMetadata metadata;
57 SkTime::DateTime now = TimeToSkTime(base::Time::Now());
58 metadata.fCreation.fEnabled = true;
59 metadata.fCreation.fDateTime = now;
60 metadata.fModified.fEnabled = true;
61 metadata.fModified.fDateTime = now;
62 const std::string& agent = printing::GetAgent();
63 metadata.fCreator = agent.empty() ? SkString("Chromium")
64 : SkString(agent.c_str(), agent.size());
65 return SkDocument::MakePDF(wStream, SK_ScalarDefaultRasterDPI, metadata,
66 nullptr, false);
thestig5ff7db22016-02-13 04:42:4567}
68
thestige3f5f5d42015-07-16 19:46:2469} // namespace
70
[email protected]8f879292011-04-08 00:21:2071namespace printing {
72
halcanary8dd6f0242016-06-13 19:40:2373struct Page {
enne7b64edf32017-02-16 20:10:0274 Page(SkSize s, sk_sp<cc::PaintRecord> c) : size_(s), content_(std::move(c)) {}
halcanary8dd6f0242016-06-13 19:40:2375 Page(Page&& that) : size_(that.size_), content_(std::move(that.content_)) {}
76 Page(const Page&) = default;
77 Page& operator=(const Page&) = default;
78 Page& operator=(Page&& that) {
79 size_ = that.size_;
80 content_ = std::move(that.content_);
81 return *this;
82 }
83 SkSize size_;
enne7b64edf32017-02-16 20:10:0284 sk_sp<cc::PaintRecord> content_;
halcanary8dd6f0242016-06-13 19:40:2385};
86
[email protected]8f879292011-04-08 00:21:2087struct PdfMetafileSkiaData {
enne7b64edf32017-02-16 20:10:0288 cc::PaintRecorder recorder_; // Current recording
halcanaryff214762015-01-22 20:34:3289
halcanary8dd6f0242016-06-13 19:40:2390 std::vector<Page> pages_;
dchengc3df9ba2016-04-07 23:09:3291 std::unique_ptr<SkStreamAsset> pdf_data_;
halcanaryff214762015-01-22 20:34:3292
halcanary223f70a2016-06-09 00:07:4293 // The scale factor is used because Blink occasionally calls
enne7b64edf32017-02-16 20:10:0294 // PaintCanvas::getTotalMatrix() even though the total matrix is not as
halcanary223f70a2016-06-09 00:07:4295 // meaningful for a vector canvas as for a raster canvas.
96 float scale_factor_;
halcanary8dd6f0242016-06-13 19:40:2397 SkSize size_;
halcanaryffa005b2016-06-15 17:13:1698 SkiaDocumentType type_;
[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,
thestig6b4461f2016-10-28 19:34:30111 size_t src_buffer_size) {
112 data_->pdf_data_ = base::MakeUnique<SkMemoryStream>(
113 src_buffer, src_buffer_size, true /* copy_data? */);
halcanaryff214762015-01-22 20:34:32114 return true;
[email protected]8f879292011-04-08 00:21:20115}
116
thestig192677ec2016-06-09 07:43:17117void PdfMetafileSkia::StartPage(const gfx::Size& page_size,
halcanary5be808e2014-11-10 22:20:05118 const gfx::Rect& content_area,
119 const float& scale_factor) {
halcanary223f70a2016-06-09 00:07:42120 DCHECK_GT(page_size.width(), 0);
121 DCHECK_GT(page_size.height(), 0);
122 DCHECK_GT(scale_factor, 0.0f);
halcanaryff214762015-01-22 20:34:32123 if (data_->recorder_.getRecordingCanvas())
thestig5ff7db22016-02-13 04:42:45124 FinishPage();
halcanaryff214762015-01-22 20:34:32125 DCHECK(!data_->recorder_.getRecordingCanvas());
halcanary223f70a2016-06-09 00:07:42126
127 float inverse_scale = 1.0 / scale_factor;
enne7b64edf32017-02-16 20:10:02128 cc::PaintCanvas* canvas = data_->recorder_.beginRecording(
halcanary223f70a2016-06-09 00:07:42129 inverse_scale * page_size.width(), inverse_scale * page_size.height());
halcanaryffa005b2016-06-15 17:13:16130 // Recording canvas is owned by the data_->recorder_. No ref() necessary.
halcanary223f70a2016-06-09 00:07:42131 if (content_area != gfx::Rect(page_size)) {
132 canvas->scale(inverse_scale, inverse_scale);
133 SkRect sk_content_area = gfx::RectToSkRect(content_area);
134 canvas->clipRect(sk_content_area);
135 canvas->translate(sk_content_area.x(), sk_content_area.y());
136 canvas->scale(scale_factor, scale_factor);
137 }
138
halcanary8dd6f0242016-06-13 19:40:23139 data_->size_ = gfx::SizeFToSkSize(gfx::SizeF(page_size));
halcanary223f70a2016-06-09 00:07:42140 data_->scale_factor_ = scale_factor;
halcanary5baa8fb42015-04-03 21:39:30141 // We scale the recording canvas's size so that
142 // canvas->getTotalMatrix() returns a value that ignores the scale
halcanary223f70a2016-06-09 00:07:42143 // factor. We store the scale factor and re-apply it later.
144 // http://crbug.com/469656
[email protected]8f879292011-04-08 00:21:20145}
146
enne7b64edf32017-02-16 20:10:02147cc::PaintCanvas* PdfMetafileSkia::GetVectorCanvasForNewPage(
halcanary5be808e2014-11-10 22:20:05148 const gfx::Size& page_size,
149 const gfx::Rect& content_area,
150 const float& scale_factor) {
halcanary223f70a2016-06-09 00:07:42151 StartPage(page_size, content_area, scale_factor);
halcanaryff214762015-01-22 20:34:32152 return data_->recorder_.getRecordingCanvas();
[email protected]8f879292011-04-08 00:21:20153}
154
155bool PdfMetafileSkia::FinishPage() {
halcanaryff214762015-01-22 20:34:32156 if (!data_->recorder_.getRecordingCanvas())
157 return false;
halcanary223f70a2016-06-09 00:07:42158
enne7b64edf32017-02-16 20:10:02159 sk_sp<cc::PaintRecord> pic = data_->recorder_.finishRecordingAsPicture();
halcanary223f70a2016-06-09 00:07:42160 if (data_->scale_factor_ != 1.0f) {
enne7b64edf32017-02-16 20:10:02161 cc::PaintCanvas* canvas = data_->recorder_.beginRecording(
162 data_->size_.width(), 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;
halcanaryffa005b2016-06-15 17:13:16180 sk_sp<SkDocument> doc;
181 switch (data_->type_) {
182 case PDF_SKIA_DOCUMENT_TYPE:
183 doc = MakePdfDocument(&stream);
184 break;
185 case MSKP_SKIA_DOCUMENT_TYPE:
186 doc = SkMakeMultiPictureDocument(&stream);
187 break;
188 }
halcanaryfb232282016-04-28 18:34:24189
halcanary8dd6f0242016-06-13 19:40:23190 for (const Page& page : data_->pages_) {
enne98c9f8052017-03-15 19:38:22191 cc::SkiaPaintCanvas canvas(
enne7b64edf32017-02-16 20:10:02192 doc->beginPage(page.size_.width(), page.size_.height()));
enned2501572017-03-09 19:59:17193 canvas.drawPicture(page.content_);
halcanary223f70a2016-06-09 00:07:42194 doc->endPage();
halcanary0aeeb3332016-03-18 14:32:39195 }
reede35e0532016-09-22 17:30:29196 doc->close();
[email protected]67e16b392011-05-30 20:58:09197
halcanaryde655aa2017-04-03 16:16:13198 data_->pdf_data_ = stream.detachAsStream();
halcanaryff214762015-01-22 20:34:32199 return true;
[email protected]8f879292011-04-08 00:21:20200}
201
thestig707a24b22015-09-14 18:16:33202uint32_t PdfMetafileSkia::GetDataSize() const {
halcanaryff214762015-01-22 20:34:32203 if (!data_->pdf_data_)
204 return 0;
thestig707a24b22015-09-14 18:16:33205 return base::checked_cast<uint32_t>(data_->pdf_data_->getLength());
[email protected]8f879292011-04-08 00:21:20206}
207
208bool PdfMetafileSkia::GetData(void* dst_buffer,
thestig707a24b22015-09-14 18:16:33209 uint32_t dst_buffer_size) const {
halcanaryff214762015-01-22 20:34:32210 if (!data_->pdf_data_)
[email protected]8f879292011-04-08 00:21:20211 return false;
halcanaryff214762015-01-22 20:34:32212 return WriteAssetToBuffer(data_->pdf_data_.get(), dst_buffer,
213 base::checked_cast<size_t>(dst_buffer_size));
[email protected]8f879292011-04-08 00:21:20214}
215
[email protected]8f879292011-04-08 00:21:20216gfx::Rect PdfMetafileSkia::GetPageBounds(unsigned int page_number) const {
halcanaryff214762015-01-22 20:34:32217 if (page_number < data_->pages_.size()) {
halcanary8dd6f0242016-06-13 19:40:23218 SkSize size = data_->pages_[page_number].size_;
219 return gfx::Rect(gfx::ToRoundedInt(size.width()),
220 gfx::ToRoundedInt(size.height()));
halcanaryff214762015-01-22 20:34:32221 }
[email protected]8f879292011-04-08 00:21:20222 return gfx::Rect();
223}
224
225unsigned int PdfMetafileSkia::GetPageCount() const {
halcanaryff214762015-01-22 20:34:32226 return base::checked_cast<unsigned int>(data_->pages_.size());
[email protected]8f879292011-04-08 00:21:20227}
228
tfarina876d1e02016-10-11 23:12:53229skia::NativeDrawingContext PdfMetafileSkia::context() const {
[email protected]8f879292011-04-08 00:21:20230 NOTREACHED();
thestig192677ec2016-06-09 07:43:17231 return nullptr;
[email protected]8f879292011-04-08 00:21:20232}
233
thestig192677ec2016-06-09 07:43:17234
[email protected]8f879292011-04-08 00:21:20235#if defined(OS_WIN)
thestig3109df12017-04-26 21:57:25236bool PdfMetafileSkia::Playback(skia::NativeDrawingContext hdc,
237 const RECT* rect) const {
238 NOTREACHED();
239 return false;
240}
241
tfarina876d1e02016-10-11 23:12:53242bool PdfMetafileSkia::SafePlayback(skia::NativeDrawingContext hdc) const {
[email protected]8f879292011-04-08 00:21:20243 NOTREACHED();
244 return false;
245}
[email protected]b8d85bc2011-06-22 13:34:57246#endif
[email protected]8f879292011-04-08 00:21:20247
halcanary5be808e2014-11-10 22:20:05248bool PdfMetafileSkia::SaveTo(base::File* file) const {
249 if (GetDataSize() == 0U)
250 return false;
halcanaryff214762015-01-22 20:34:32251
252 // Calling duplicate() keeps original asset state unchanged.
dchengc3df9ba2016-04-07 23:09:32253 std::unique_ptr<SkStreamAsset> asset(data_->pdf_data_->duplicate());
halcanaryff214762015-01-22 20:34:32254
thestig192677ec2016-06-09 07:43:17255 const size_t kMaximumBufferSize = 1024 * 1024;
256 std::vector<char> buffer(std::min(kMaximumBufferSize, asset->getLength()));
halcanaryff214762015-01-22 20:34:32257 do {
258 size_t read_size = asset->read(&buffer[0], buffer.size());
259 if (read_size == 0)
260 break;
261 DCHECK_GE(buffer.size(), read_size);
262 if (!file->WriteAtCurrentPos(&buffer[0],
263 base::checked_cast<int>(read_size))) {
264 return false;
265 }
266 } while (!asset->isAtEnd());
267
268 return true;
halcanary5be808e2014-11-10 22:20:05269}
270
halcanaryffa005b2016-06-15 17:13:16271PdfMetafileSkia::PdfMetafileSkia(SkiaDocumentType type)
272 : data_(new PdfMetafileSkiaData) {
273 data_->type_ = type;
[email protected]19b9d3b2011-07-23 02:08:57274}
[email protected]597516372011-07-01 05:10:44275
halcanaryffa005b2016-06-15 17:13:16276std::unique_ptr<PdfMetafileSkia> PdfMetafileSkia::GetMetafileForCurrentPage(
277 SkiaDocumentType type) {
halcanaryff214762015-01-22 20:34:32278 // If we only ever need the metafile for the last page, should we
enne7b64edf32017-02-16 20:10:02279 // only keep a handle on one PaintRecord?
halcanaryffa005b2016-06-15 17:13:16280 std::unique_ptr<PdfMetafileSkia> metafile(new PdfMetafileSkia(type));
halcanaryff214762015-01-22 20:34:32281
282 if (data_->pages_.size() == 0)
dchenge48600452015-12-28 02:24:50283 return metafile;
[email protected]597516372011-07-01 05:10:44284
halcanaryff214762015-01-22 20:34:32285 if (data_->recorder_.getRecordingCanvas()) // page outstanding
dchenge48600452015-12-28 02:24:50286 return metafile;
[email protected]597516372011-07-01 05:10:44287
halcanary223f70a2016-06-09 00:07:42288 metafile->data_->pages_.push_back(data_->pages_.back());
halcanaryff214762015-01-22 20:34:32289
290 if (!metafile->FinishDocument()) // Generate PDF.
vitalybuka5d1290582014-09-12 09:19:59291 metafile.reset();
halcanaryff214762015-01-22 20:34:32292
dchenge48600452015-12-28 02:24:50293 return metafile;
[email protected]597516372011-07-01 05:10:44294}
[email protected]8f879292011-04-08 00:21:20295
[email protected]8f879292011-04-08 00:21:20296} // namespace printing