metafile_skia.cc 16 KB

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