Upload upstream chromium 73.0.3683.0
[platform/framework/web/chromium-efl.git] / printing / metafile_skia.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // 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/metafile_skia.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <utility>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/files/file.h"
15 #include "base/stl_util.h"
16 #include "base/time/time.h"
17 #include "cc/paint/paint_record.h"
18 #include "cc/paint/paint_recorder.h"
19 #include "cc/paint/skia_paint_canvas.h"
20 #include "printing/print_settings.h"
21 #include "third_party/skia/include/core/SkCanvas.h"
22 #include "third_party/skia/include/core/SkPicture.h"
23 #include "third_party/skia/include/core/SkSerialProcs.h"
24 #include "third_party/skia/include/core/SkStream.h"
25 // Note that headers in third_party/skia/src are fragile.  This is
26 // an experimental, fragile, and diagnostic-only document type.
27 #include "third_party/skia/src/utils/SkMultiPictureDocument.h"
28 #include "ui/gfx/geometry/safe_integer_conversions.h"
29 #include "ui/gfx/skia_util.h"
30
31 #if defined(OS_MACOSX)
32 #include "printing/pdf_metafile_cg_mac.h"
33 #endif
34
35 #if defined(OS_POSIX)
36 #include "base/file_descriptor_posix.h"
37 #endif
38
39 namespace {
40
41 bool WriteAssetToBuffer(const SkStreamAsset* asset, void* buffer, size_t size) {
42   // Calling duplicate() keeps original asset state unchanged.
43   std::unique_ptr<SkStreamAsset> assetCopy(asset->duplicate());
44   size_t length = assetCopy->getLength();
45   return length <= size && length == assetCopy->read(buffer, length);
46 }
47
48 }  // namespace
49
50 namespace printing {
51
52 struct Page {
53   Page(const SkSize& s, sk_sp<cc::PaintRecord> c)
54       : size(s), content(std::move(c)) {}
55   Page(Page&& that) : size(that.size), content(std::move(that.content)) {}
56   Page(const Page&) = default;
57   Page& operator=(const Page&) = default;
58   Page& operator=(Page&& that) {
59     size = that.size;
60     content = std::move(that.content);
61     return *this;
62   }
63   SkSize size;
64   sk_sp<cc::PaintRecord> content;
65 };
66
67 struct MetafileSkiaData {
68   cc::PaintRecorder recorder;  // Current recording
69
70   std::vector<Page> pages;
71   std::unique_ptr<SkStreamAsset> data_stream;
72   ContentToProxyIdMap subframe_content_info;
73   std::map<uint32_t, sk_sp<SkPicture>> subframe_pics;
74   int document_cookie = 0;
75
76   // The scale factor is used because Blink occasionally calls
77   // PaintCanvas::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   SkSize size;
81   SkiaDocumentType type;
82
83 #if defined(OS_MACOSX)
84   PdfMetafileCg pdf_cg;
85 #endif
86 };
87
88 MetafileSkia::MetafileSkia() : data_(std::make_unique<MetafileSkiaData>()) {
89   data_->type = SkiaDocumentType::PDF;
90 }
91
92 MetafileSkia::MetafileSkia(SkiaDocumentType type, int document_cookie)
93     : data_(std::make_unique<MetafileSkiaData>()) {
94   data_->type = type;
95   data_->document_cookie = document_cookie;
96 }
97
98 MetafileSkia::~MetafileSkia() = default;
99
100 bool MetafileSkia::Init() {
101   return true;
102 }
103
104 // TODO(halcanary): Create a Metafile class that only stores data.
105 // Metafile::InitFromData is orthogonal to what the rest of
106 // MetafileSkia does.
107 bool MetafileSkia::InitFromData(const void* src_buffer,
108                                 size_t src_buffer_size) {
109   data_->data_stream = std::make_unique<SkMemoryStream>(
110       src_buffer, src_buffer_size, true /* copy_data? */);
111   return true;
112 }
113
114 void MetafileSkia::StartPage(const gfx::Size& page_size,
115                              const gfx::Rect& content_area,
116                              const float& scale_factor) {
117   DCHECK_GT(page_size.width(), 0);
118   DCHECK_GT(page_size.height(), 0);
119   DCHECK_GT(scale_factor, 0.0f);
120   if (data_->recorder.getRecordingCanvas())
121     FinishPage();
122   DCHECK(!data_->recorder.getRecordingCanvas());
123
124   float inverse_scale = 1.0 / scale_factor;
125   cc::PaintCanvas* canvas = data_->recorder.beginRecording(
126       inverse_scale * page_size.width(), inverse_scale * page_size.height());
127   // Recording canvas is owned by the |data_->recorder|.  No ref() necessary.
128   if (content_area != gfx::Rect(page_size)) {
129     canvas->scale(inverse_scale, inverse_scale);
130     SkRect sk_content_area = gfx::RectToSkRect(content_area);
131     canvas->clipRect(sk_content_area);
132     canvas->translate(sk_content_area.x(), sk_content_area.y());
133     canvas->scale(scale_factor, scale_factor);
134   }
135
136   data_->size = gfx::SizeFToSkSize(gfx::SizeF(page_size));
137   data_->scale_factor = scale_factor;
138   // We scale the recording canvas's size so that
139   // canvas->getTotalMatrix() returns a value that ignores the scale
140   // factor.  We store the scale factor and re-apply it later.
141   // http://crbug.com/469656
142 }
143
144 cc::PaintCanvas* MetafileSkia::GetVectorCanvasForNewPage(
145     const gfx::Size& page_size,
146     const gfx::Rect& content_area,
147     const float& scale_factor) {
148   StartPage(page_size, content_area, scale_factor);
149   return data_->recorder.getRecordingCanvas();
150 }
151
152 bool MetafileSkia::FinishPage() {
153   if (!data_->recorder.getRecordingCanvas())
154     return false;
155
156   sk_sp<cc::PaintRecord> pic = data_->recorder.finishRecordingAsPicture();
157   if (data_->scale_factor != 1.0f) {
158     cc::PaintCanvas* canvas = data_->recorder.beginRecording(
159         data_->size.width(), data_->size.height());
160     canvas->scale(data_->scale_factor, data_->scale_factor);
161     canvas->drawPicture(pic);
162     pic = data_->recorder.finishRecordingAsPicture();
163   }
164   data_->pages.emplace_back(data_->size, std::move(pic));
165   return true;
166 }
167
168 bool MetafileSkia::FinishDocument() {
169   // If we've already set the data in InitFromData, leave it be.
170   if (data_->data_stream)
171     return false;
172
173   if (data_->recorder.getRecordingCanvas())
174     FinishPage();
175
176   SkDynamicMemoryWStream stream;
177   sk_sp<SkDocument> doc;
178   cc::PlaybackParams::CustomDataRasterCallback custom_callback;
179   switch (data_->type) {
180     case SkiaDocumentType::PDF:
181       doc = MakePdfDocument(printing::GetAgent(), &stream);
182       break;
183     case SkiaDocumentType::MSKP:
184       SkSerialProcs procs = SerializationProcs(&data_->subframe_content_info);
185       doc = SkMakeMultiPictureDocument(&stream, &procs);
186       // It is safe to use base::Unretained(this) because the callback
187       // is only used by |canvas| in the following loop which has shorter
188       // lifetime than |this|.
189       custom_callback = base::BindRepeating(
190           &MetafileSkia::CustomDataToSkPictureCallback, base::Unretained(this));
191       break;
192   }
193
194   for (const Page& page : data_->pages) {
195     cc::SkiaPaintCanvas canvas(
196         doc->beginPage(page.size.width(), page.size.height()));
197     canvas.drawPicture(page.content, custom_callback);
198     doc->endPage();
199   }
200   doc->close();
201
202   data_->data_stream = stream.detachAsStream();
203   return true;
204 }
205
206 void MetafileSkia::FinishFrameContent() {
207   // Sanity check to make sure we print the entire frame as a single page
208   // content.
209   DCHECK_EQ(data_->pages.size(), 1u);
210   // Also make sure it is in skia multi-picture document format.
211   DCHECK_EQ(data_->type, SkiaDocumentType::MSKP);
212   DCHECK(!data_->data_stream);
213
214   cc::PlaybackParams::CustomDataRasterCallback custom_callback =
215       base::BindRepeating(&MetafileSkia::CustomDataToSkPictureCallback,
216                           base::Unretained(this));
217   sk_sp<SkPicture> pic = ToSkPicture(data_->pages[0].content,
218                                      SkRect::MakeSize(data_->pages[0].size),
219                                      nullptr, custom_callback);
220   SkSerialProcs procs = SerializationProcs(&data_->subframe_content_info);
221   SkDynamicMemoryWStream stream;
222   pic->serialize(&stream, &procs);
223   data_->data_stream = stream.detachAsStream();
224 }
225
226 uint32_t MetafileSkia::GetDataSize() const {
227   if (!data_->data_stream)
228     return 0;
229   return base::checked_cast<uint32_t>(data_->data_stream->getLength());
230 }
231
232 bool MetafileSkia::GetData(void* dst_buffer, uint32_t dst_buffer_size) const {
233   if (!data_->data_stream)
234     return false;
235   return WriteAssetToBuffer(data_->data_stream.get(), dst_buffer,
236                             base::checked_cast<size_t>(dst_buffer_size));
237 }
238
239 gfx::Rect MetafileSkia::GetPageBounds(unsigned int page_number) const {
240   if (page_number < data_->pages.size()) {
241     SkSize size = data_->pages[page_number].size;
242     return gfx::Rect(gfx::ToRoundedInt(size.width()),
243                      gfx::ToRoundedInt(size.height()));
244   }
245   return gfx::Rect();
246 }
247
248 unsigned int MetafileSkia::GetPageCount() const {
249   return base::checked_cast<unsigned int>(data_->pages.size());
250 }
251
252 printing::NativeDrawingContext MetafileSkia::context() const {
253   NOTREACHED();
254   return nullptr;
255 }
256
257 #if defined(OS_WIN)
258 bool MetafileSkia::Playback(printing::NativeDrawingContext hdc,
259                             const RECT* rect) const {
260   NOTREACHED();
261   return false;
262 }
263
264 bool MetafileSkia::SafePlayback(printing::NativeDrawingContext hdc) const {
265   NOTREACHED();
266   return false;
267 }
268
269 #elif defined(OS_MACOSX)
270 /* TODO(caryclark): The set up of PluginInstance::PrintPDFOutput may result in
271    rasterized output.  Even if that flow uses PdfMetafileCg::RenderPage,
272    the drawing of the PDF into the canvas may result in a rasterized output.
273    PDFMetafileSkia::RenderPage should be not implemented as shown and instead
274    should do something like the following CL in PluginInstance::PrintPDFOutput:
275 http://codereview.chromium.org/7200040/diff/1/webkit/plugins/ppapi/ppapi_plugin_instance.cc
276 */
277 bool MetafileSkia::RenderPage(unsigned int page_number,
278                               CGContextRef context,
279                               const CGRect rect,
280                               const MacRenderPageParams& params) const {
281   DCHECK_GT(GetDataSize(), 0U);
282   if (data_->pdf_cg.GetDataSize() == 0) {
283     if (GetDataSize() == 0)
284       return false;
285     size_t length = data_->data_stream->getLength();
286     std::vector<uint8_t> buffer(length);
287     (void)WriteAssetToBuffer(data_->data_stream.get(), &buffer[0], length);
288     data_->pdf_cg.InitFromData(&buffer[0], length);
289   }
290   return data_->pdf_cg.RenderPage(page_number, context, rect, params);
291 }
292 #endif
293
294 bool MetafileSkia::SaveTo(base::File* file) const {
295   if (GetDataSize() == 0U)
296     return false;
297
298   // Calling duplicate() keeps original asset state unchanged.
299   std::unique_ptr<SkStreamAsset> asset(data_->data_stream->duplicate());
300
301   static constexpr size_t kMaximumBufferSize = 1024 * 1024;
302   std::vector<char> buffer(std::min(kMaximumBufferSize, asset->getLength()));
303   do {
304     size_t read_size = asset->read(&buffer[0], buffer.size());
305     if (read_size == 0)
306       break;
307     DCHECK_GE(buffer.size(), read_size);
308     if (!file->WriteAtCurrentPos(&buffer[0],
309                                  base::checked_cast<int>(read_size))) {
310       return false;
311     }
312   } while (!asset->isAtEnd());
313
314   return true;
315 }
316
317 std::unique_ptr<MetafileSkia> MetafileSkia::GetMetafileForCurrentPage(
318     SkiaDocumentType type) {
319   // If we only ever need the metafile for the last page, should we
320   // only keep a handle on one PaintRecord?
321   auto metafile = std::make_unique<MetafileSkia>(type, data_->document_cookie);
322   if (data_->pages.size() == 0)
323     return metafile;
324
325   if (data_->recorder.getRecordingCanvas())  // page outstanding
326     return metafile;
327
328   metafile->data_->pages.push_back(data_->pages.back());
329   metafile->data_->subframe_content_info = data_->subframe_content_info;
330   metafile->data_->subframe_pics = data_->subframe_pics;
331
332   if (!metafile->FinishDocument())  // Generate PDF.
333     metafile.reset();
334
335   return metafile;
336 }
337
338 uint32_t MetafileSkia::CreateContentForRemoteFrame(const gfx::Rect& rect,
339                                                    int render_proxy_id) {
340   // Create a place holder picture.
341   sk_sp<SkPicture> pic = SkPicture::MakePlaceholder(
342       SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()));
343
344   // Store the map between content id and the proxy id.
345   uint32_t content_id = pic->uniqueID();
346   DCHECK(!base::ContainsKey(data_->subframe_content_info, content_id));
347   data_->subframe_content_info[content_id] = render_proxy_id;
348
349   // Store the picture content.
350   data_->subframe_pics[content_id] = pic;
351   return content_id;
352 }
353
354 int MetafileSkia::GetDocumentCookie() const {
355   return data_->document_cookie;
356 }
357
358 const ContentToProxyIdMap& MetafileSkia::GetSubframeContentInfo() const {
359   return data_->subframe_content_info;
360 }
361
362 void MetafileSkia::AppendPage(const SkSize& page_size,
363                               sk_sp<cc::PaintRecord> record) {
364   data_->pages.emplace_back(page_size, std::move(record));
365 }
366
367 void MetafileSkia::AppendSubframeInfo(uint32_t content_id,
368                                       int proxy_id,
369                                       sk_sp<SkPicture> pic_holder) {
370   data_->subframe_content_info[content_id] = proxy_id;
371   data_->subframe_pics[content_id] = pic_holder;
372 }
373
374 SkStreamAsset* MetafileSkia::GetPdfData() const {
375   return data_->data_stream.get();
376 }
377
378 void MetafileSkia::CustomDataToSkPictureCallback(SkCanvas* canvas,
379                                                  uint32_t content_id) {
380   // Check whether this is the one we need to handle.
381   if (!base::ContainsKey(data_->subframe_content_info, content_id))
382     return;
383
384   auto it = data_->subframe_pics.find(content_id);
385   DCHECK(it != data_->subframe_pics.end());
386
387   // Found the picture, draw it on canvas.
388   sk_sp<SkPicture> pic = it->second;
389   SkRect rect = pic->cullRect();
390   SkMatrix matrix = SkMatrix::MakeTrans(rect.x(), rect.y());
391   canvas->drawPicture(it->second, &matrix, nullptr);
392 }
393
394 }  // namespace printing