blob: c05e621290347084484e489384072fba7f21c335 [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"
halcanary67ce00b82015-09-27 21:59:5313#include "base/time/time.h"
enne7b64edf32017-02-16 20:10:0214#include "cc/paint/paint_record.h"
15#include "cc/paint/paint_recorder.h"
enne59df29de2017-05-02 03:23:3716#include "cc/paint/skia_paint_canvas.h"
halcanary73b63fd2015-11-06 00:02:1417#include "printing/print_settings.h"
Wei Li5bb659742018-02-14 03:07:5818#include "third_party/skia/include/core/SkSerialProcs.h"
[email protected]8f879292011-04-08 00:21:2019#include "third_party/skia/include/core/SkStream.h"
halcanaryffa005b2016-06-15 17:13:1620// Note that headers in third_party/skia/src are fragile. This is
21// an experimental, fragile, and diagnostic-only document type.
22#include "third_party/skia/src/utils/SkMultiPictureDocument.h"
halcanary223f70a2016-06-09 00:07:4223#include "ui/gfx/geometry/safe_integer_conversions.h"
halcanaryff214762015-01-22 20:34:3224#include "ui/gfx/skia_util.h"
[email protected]8f879292011-04-08 00:21:2025
thestig99fc2132017-04-27 03:37:5426#if defined(OS_MACOSX)
27#include "printing/pdf_metafile_cg_mac.h"
28#endif
29
30#if defined(OS_POSIX)
31#include "base/file_descriptor_posix.h"
32#endif
33
halcanaryff214762015-01-22 20:34:3234namespace {
thestige3f5f5d42015-07-16 19:46:2435
thestige3f5f5d42015-07-16 19:46:2436bool WriteAssetToBuffer(const SkStreamAsset* asset,
37 void* buffer,
38 size_t size) {
halcanaryff214762015-01-22 20:34:3239 // Calling duplicate() keeps original asset state unchanged.
dchengc3df9ba2016-04-07 23:09:3240 std::unique_ptr<SkStreamAsset> assetCopy(asset->duplicate());
halcanaryff214762015-01-22 20:34:3241 size_t length = assetCopy->getLength();
Lei Zhang662e4222017-12-14 20:18:3442 return length <= size && length == assetCopy->read(buffer, length);
halcanaryff214762015-01-22 20:34:3243}
44
thestige3f5f5d42015-07-16 19:46:2445} // namespace
46
[email protected]8f879292011-04-08 00:21:2047namespace printing {
48
Lei Zhang662e4222017-12-14 20:18:3449// TODO(thestig): struct members should not have trailing underscore.
halcanary8dd6f0242016-06-13 19:40:2350struct Page {
enne7b64edf32017-02-16 20:10:0251 Page(SkSize s, sk_sp<cc::PaintRecord> c) : size_(s), content_(std::move(c)) {}
halcanary8dd6f0242016-06-13 19:40:2352 Page(Page&& that) : size_(that.size_), content_(std::move(that.content_)) {}
53 Page(const Page&) = default;
54 Page& operator=(const Page&) = default;
55 Page& operator=(Page&& that) {
56 size_ = that.size_;
57 content_ = std::move(that.content_);
58 return *this;
59 }
60 SkSize size_;
enne7b64edf32017-02-16 20:10:0261 sk_sp<cc::PaintRecord> content_;
halcanary8dd6f0242016-06-13 19:40:2362};
63
Wei Li5bb659742018-02-14 03:07:5864// TODO(weili): Remove pdf from struct name and field names since it is used for
65// other formats as well.
[email protected]8f879292011-04-08 00:21:2066struct PdfMetafileSkiaData {
enne7b64edf32017-02-16 20:10:0267 cc::PaintRecorder recorder_; // Current recording
halcanaryff214762015-01-22 20:34:3268
halcanary8dd6f0242016-06-13 19:40:2369 std::vector<Page> pages_;
dchengc3df9ba2016-04-07 23:09:3270 std::unique_ptr<SkStreamAsset> pdf_data_;
halcanaryff214762015-01-22 20:34:3271
halcanary223f70a2016-06-09 00:07:4272 // The scale factor is used because Blink occasionally calls
enne7b64edf32017-02-16 20:10:0273 // PaintCanvas::getTotalMatrix() even though the total matrix is not as
halcanary223f70a2016-06-09 00:07:4274 // meaningful for a vector canvas as for a raster canvas.
75 float scale_factor_;
halcanary8dd6f0242016-06-13 19:40:2376 SkSize size_;
halcanaryffa005b2016-06-15 17:13:1677 SkiaDocumentType type_;
thestig99fc2132017-04-27 03:37:5478
79#if defined(OS_MACOSX)
80 PdfMetafileCg pdf_cg_;
81#endif
[email protected]8f879292011-04-08 00:21:2082};
83
Lei Zhang662e4222017-12-14 20:18:3484PdfMetafileSkia::PdfMetafileSkia(SkiaDocumentType type)
85 : data_(std::make_unique<PdfMetafileSkiaData>()) {
86 data_->type_ = type;
87}
88
Chris Watkinsc360b972017-12-01 05:50:2389PdfMetafileSkia::~PdfMetafileSkia() = default;
[email protected]8f879292011-04-08 00:21:2090
91bool PdfMetafileSkia::Init() {
92 return true;
93}
halcanaryff214762015-01-22 20:34:3294
95// TODO(halcanary): Create a Metafile class that only stores data.
96// Metafile::InitFromData is orthogonal to what the rest of
97// PdfMetafileSkia does.
[email protected]8f879292011-04-08 00:21:2098bool PdfMetafileSkia::InitFromData(const void* src_buffer,
thestig6b4461f2016-10-28 19:34:3099 size_t src_buffer_size) {
Lei Zhang662e4222017-12-14 20:18:34100 data_->pdf_data_ = std::make_unique<SkMemoryStream>(
thestig6b4461f2016-10-28 19:34:30101 src_buffer, src_buffer_size, true /* copy_data? */);
halcanaryff214762015-01-22 20:34:32102 return true;
[email protected]8f879292011-04-08 00:21:20103}
104
thestig192677ec2016-06-09 07:43:17105void PdfMetafileSkia::StartPage(const gfx::Size& page_size,
halcanary5be808e2014-11-10 22:20:05106 const gfx::Rect& content_area,
107 const float& scale_factor) {
halcanary223f70a2016-06-09 00:07:42108 DCHECK_GT(page_size.width(), 0);
109 DCHECK_GT(page_size.height(), 0);
110 DCHECK_GT(scale_factor, 0.0f);
halcanaryff214762015-01-22 20:34:32111 if (data_->recorder_.getRecordingCanvas())
thestig5ff7db22016-02-13 04:42:45112 FinishPage();
halcanaryff214762015-01-22 20:34:32113 DCHECK(!data_->recorder_.getRecordingCanvas());
halcanary223f70a2016-06-09 00:07:42114
115 float inverse_scale = 1.0 / scale_factor;
enne7b64edf32017-02-16 20:10:02116 cc::PaintCanvas* canvas = data_->recorder_.beginRecording(
halcanary223f70a2016-06-09 00:07:42117 inverse_scale * page_size.width(), inverse_scale * page_size.height());
halcanaryffa005b2016-06-15 17:13:16118 // Recording canvas is owned by the data_->recorder_. No ref() necessary.
halcanary223f70a2016-06-09 00:07:42119 if (content_area != gfx::Rect(page_size)) {
120 canvas->scale(inverse_scale, inverse_scale);
121 SkRect sk_content_area = gfx::RectToSkRect(content_area);
122 canvas->clipRect(sk_content_area);
123 canvas->translate(sk_content_area.x(), sk_content_area.y());
124 canvas->scale(scale_factor, scale_factor);
125 }
126
halcanary8dd6f0242016-06-13 19:40:23127 data_->size_ = gfx::SizeFToSkSize(gfx::SizeF(page_size));
halcanary223f70a2016-06-09 00:07:42128 data_->scale_factor_ = scale_factor;
halcanary5baa8fb42015-04-03 21:39:30129 // We scale the recording canvas's size so that
130 // canvas->getTotalMatrix() returns a value that ignores the scale
halcanary223f70a2016-06-09 00:07:42131 // factor. We store the scale factor and re-apply it later.
132 // http://crbug.com/469656
[email protected]8f879292011-04-08 00:21:20133}
134
enne7b64edf32017-02-16 20:10:02135cc::PaintCanvas* PdfMetafileSkia::GetVectorCanvasForNewPage(
halcanary5be808e2014-11-10 22:20:05136 const gfx::Size& page_size,
137 const gfx::Rect& content_area,
138 const float& scale_factor) {
halcanary223f70a2016-06-09 00:07:42139 StartPage(page_size, content_area, scale_factor);
halcanaryff214762015-01-22 20:34:32140 return data_->recorder_.getRecordingCanvas();
[email protected]8f879292011-04-08 00:21:20141}
142
143bool PdfMetafileSkia::FinishPage() {
halcanaryff214762015-01-22 20:34:32144 if (!data_->recorder_.getRecordingCanvas())
145 return false;
halcanary223f70a2016-06-09 00:07:42146
enne7b64edf32017-02-16 20:10:02147 sk_sp<cc::PaintRecord> pic = data_->recorder_.finishRecordingAsPicture();
halcanary223f70a2016-06-09 00:07:42148 if (data_->scale_factor_ != 1.0f) {
enne7b64edf32017-02-16 20:10:02149 cc::PaintCanvas* canvas = data_->recorder_.beginRecording(
150 data_->size_.width(), data_->size_.height());
halcanary223f70a2016-06-09 00:07:42151 canvas->scale(data_->scale_factor_, data_->scale_factor_);
152 canvas->drawPicture(pic);
153 pic = data_->recorder_.finishRecordingAsPicture();
154 }
halcanary8dd6f0242016-06-13 19:40:23155 data_->pages_.emplace_back(data_->size_, std::move(pic));
[email protected]8f879292011-04-08 00:21:20156 return true;
157}
158
159bool PdfMetafileSkia::FinishDocument() {
halcanaryff214762015-01-22 20:34:32160 // If we've already set the data in InitFromData, leave it be.
161 if (data_->pdf_data_)
162 return false;
[email protected]8f879292011-04-08 00:21:20163
halcanaryff214762015-01-22 20:34:32164 if (data_->recorder_.getRecordingCanvas())
thestig5ff7db22016-02-13 04:42:45165 FinishPage();
[email protected]67e16b392011-05-30 20:58:09166
halcanary223f70a2016-06-09 00:07:42167 SkDynamicMemoryWStream stream;
halcanaryffa005b2016-06-15 17:13:16168 sk_sp<SkDocument> doc;
169 switch (data_->type_) {
Wei Lid0e05022017-08-22 03:56:52170 case SkiaDocumentType::PDF:
weilifabbf7572017-05-22 19:05:16171 doc = MakePdfDocument(printing::GetAgent(), &stream);
halcanaryffa005b2016-06-15 17:13:16172 break;
Wei Lid0e05022017-08-22 03:56:52173 case SkiaDocumentType::MSKP:
halcanaryffa005b2016-06-15 17:13:16174 doc = SkMakeMultiPictureDocument(&stream);
175 break;
176 }
halcanaryfb232282016-04-28 18:34:24177
halcanary8dd6f0242016-06-13 19:40:23178 for (const Page& page : data_->pages_) {
enne98c9f8052017-03-15 19:38:22179 cc::SkiaPaintCanvas canvas(
enne7b64edf32017-02-16 20:10:02180 doc->beginPage(page.size_.width(), page.size_.height()));
enned2501572017-03-09 19:59:17181 canvas.drawPicture(page.content_);
halcanary223f70a2016-06-09 00:07:42182 doc->endPage();
halcanary0aeeb3332016-03-18 14:32:39183 }
reede35e0532016-09-22 17:30:29184 doc->close();
[email protected]67e16b392011-05-30 20:58:09185
halcanaryde655aa2017-04-03 16:16:13186 data_->pdf_data_ = stream.detachAsStream();
halcanaryff214762015-01-22 20:34:32187 return true;
[email protected]8f879292011-04-08 00:21:20188}
189
Wei Li5bb659742018-02-14 03:07:58190void PdfMetafileSkia::FinishFrameContent() {
191 // Sanity check to make sure we print the entire frame as a single page
192 // content.
193 DCHECK_EQ(data_->pages_.size(), 1u);
194 // Also make sure it is in skia multi-picture document format.
195 DCHECK_EQ(data_->type_, SkiaDocumentType::MSKP);
196 DCHECK(!data_->pdf_data_);
197
198 SkDynamicMemoryWStream stream;
199 sk_sp<SkPicture> pic = ToSkPicture(data_->pages_[0].content_,
200 SkRect::MakeSize(data_->pages_[0].size_));
201 SkSerialProcs procs;
202 pic->serialize(&stream, &procs);
203 data_->pdf_data_ = stream.detachAsStream();
204}
205
thestig707a24b22015-09-14 18:16:33206uint32_t PdfMetafileSkia::GetDataSize() const {
halcanaryff214762015-01-22 20:34:32207 if (!data_->pdf_data_)
208 return 0;
thestig707a24b22015-09-14 18:16:33209 return base::checked_cast<uint32_t>(data_->pdf_data_->getLength());
[email protected]8f879292011-04-08 00:21:20210}
211
212bool PdfMetafileSkia::GetData(void* dst_buffer,
thestig707a24b22015-09-14 18:16:33213 uint32_t dst_buffer_size) const {
halcanaryff214762015-01-22 20:34:32214 if (!data_->pdf_data_)
[email protected]8f879292011-04-08 00:21:20215 return false;
halcanaryff214762015-01-22 20:34:32216 return WriteAssetToBuffer(data_->pdf_data_.get(), dst_buffer,
217 base::checked_cast<size_t>(dst_buffer_size));
[email protected]8f879292011-04-08 00:21:20218}
219
[email protected]8f879292011-04-08 00:21:20220gfx::Rect PdfMetafileSkia::GetPageBounds(unsigned int page_number) const {
halcanaryff214762015-01-22 20:34:32221 if (page_number < data_->pages_.size()) {
halcanary8dd6f0242016-06-13 19:40:23222 SkSize size = data_->pages_[page_number].size_;
223 return gfx::Rect(gfx::ToRoundedInt(size.width()),
224 gfx::ToRoundedInt(size.height()));
halcanaryff214762015-01-22 20:34:32225 }
[email protected]8f879292011-04-08 00:21:20226 return gfx::Rect();
227}
228
229unsigned int PdfMetafileSkia::GetPageCount() const {
halcanaryff214762015-01-22 20:34:32230 return base::checked_cast<unsigned int>(data_->pages_.size());
[email protected]8f879292011-04-08 00:21:20231}
232
Nico Weber8e559562017-10-03 01:25:26233printing::NativeDrawingContext PdfMetafileSkia::context() const {
[email protected]8f879292011-04-08 00:21:20234 NOTREACHED();
thestig192677ec2016-06-09 07:43:17235 return nullptr;
[email protected]8f879292011-04-08 00:21:20236}
237
thestig192677ec2016-06-09 07:43:17238
[email protected]8f879292011-04-08 00:21:20239#if defined(OS_WIN)
Nico Weber8e559562017-10-03 01:25:26240bool PdfMetafileSkia::Playback(printing::NativeDrawingContext hdc,
thestig3109df12017-04-26 21:57:25241 const RECT* rect) const {
242 NOTREACHED();
243 return false;
244}
245
Nico Weber8e559562017-10-03 01:25:26246bool PdfMetafileSkia::SafePlayback(printing::NativeDrawingContext hdc) const {
[email protected]8f879292011-04-08 00:21:20247 NOTREACHED();
248 return false;
249}
thestig99fc2132017-04-27 03:37:54250
251#elif defined(OS_MACOSX)
252/* TODO(caryclark): The set up of PluginInstance::PrintPDFOutput may result in
253 rasterized output. Even if that flow uses PdfMetafileCg::RenderPage,
254 the drawing of the PDF into the canvas may result in a rasterized output.
255 PDFMetafileSkia::RenderPage should be not implemented as shown and instead
256 should do something like the following CL in PluginInstance::PrintPDFOutput:
257http://codereview.chromium.org/7200040/diff/1/webkit/plugins/ppapi/ppapi_plugin_instance.cc
258*/
259bool PdfMetafileSkia::RenderPage(unsigned int page_number,
260 CGContextRef context,
261 const CGRect rect,
262 const MacRenderPageParams& params) const {
263 DCHECK_GT(GetDataSize(), 0U);
264 if (data_->pdf_cg_.GetDataSize() == 0) {
265 if (GetDataSize() == 0)
266 return false;
267 size_t length = data_->pdf_data_->getLength();
268 std::vector<uint8_t> buffer(length);
269 (void)WriteAssetToBuffer(data_->pdf_data_.get(), &buffer[0], length);
270 data_->pdf_cg_.InitFromData(&buffer[0], length);
271 }
272 return data_->pdf_cg_.RenderPage(page_number, context, rect, params);
273}
[email protected]b8d85bc2011-06-22 13:34:57274#endif
[email protected]8f879292011-04-08 00:21:20275
halcanary5be808e2014-11-10 22:20:05276bool PdfMetafileSkia::SaveTo(base::File* file) const {
277 if (GetDataSize() == 0U)
278 return false;
halcanaryff214762015-01-22 20:34:32279
280 // Calling duplicate() keeps original asset state unchanged.
dchengc3df9ba2016-04-07 23:09:32281 std::unique_ptr<SkStreamAsset> asset(data_->pdf_data_->duplicate());
halcanaryff214762015-01-22 20:34:32282
thestig192677ec2016-06-09 07:43:17283 const size_t kMaximumBufferSize = 1024 * 1024;
284 std::vector<char> buffer(std::min(kMaximumBufferSize, asset->getLength()));
halcanaryff214762015-01-22 20:34:32285 do {
286 size_t read_size = asset->read(&buffer[0], buffer.size());
287 if (read_size == 0)
288 break;
289 DCHECK_GE(buffer.size(), read_size);
290 if (!file->WriteAtCurrentPos(&buffer[0],
291 base::checked_cast<int>(read_size))) {
292 return false;
293 }
294 } while (!asset->isAtEnd());
295
296 return true;
halcanary5be808e2014-11-10 22:20:05297}
298
halcanaryffa005b2016-06-15 17:13:16299std::unique_ptr<PdfMetafileSkia> PdfMetafileSkia::GetMetafileForCurrentPage(
300 SkiaDocumentType type) {
halcanaryff214762015-01-22 20:34:32301 // If we only ever need the metafile for the last page, should we
enne7b64edf32017-02-16 20:10:02302 // only keep a handle on one PaintRecord?
Lei Zhang662e4222017-12-14 20:18:34303 auto metafile = std::make_unique<PdfMetafileSkia>(type);
halcanaryff214762015-01-22 20:34:32304 if (data_->pages_.size() == 0)
dchenge48600452015-12-28 02:24:50305 return metafile;
[email protected]597516372011-07-01 05:10:44306
halcanaryff214762015-01-22 20:34:32307 if (data_->recorder_.getRecordingCanvas()) // page outstanding
dchenge48600452015-12-28 02:24:50308 return metafile;
[email protected]597516372011-07-01 05:10:44309
halcanary223f70a2016-06-09 00:07:42310 metafile->data_->pages_.push_back(data_->pages_.back());
halcanaryff214762015-01-22 20:34:32311
312 if (!metafile->FinishDocument()) // Generate PDF.
vitalybuka5d1290582014-09-12 09:19:59313 metafile.reset();
halcanaryff214762015-01-22 20:34:32314
dchenge48600452015-12-28 02:24:50315 return metafile;
[email protected]597516372011-07-01 05:10:44316}
[email protected]8f879292011-04-08 00:21:20317
[email protected]8f879292011-04-08 00:21:20318} // namespace printing