| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/printing/service/pdf_compositor_impl.h" |
| |
| #include <tuple> |
| |
| #include "base/logging.h" |
| #include "base/memory/shared_memory_handle.h" |
| #include "components/printing/service/public/cpp/pdf_service_mojo_types.h" |
| #include "components/printing/service/public/cpp/pdf_service_mojo_utils.h" |
| #include "mojo/public/cpp/system/platform_handle.h" |
| #include "printing/common/pdf_metafile_utils.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkDocument.h" |
| #include "third_party/skia/include/core/SkSerialProcs.h" |
| #include "third_party/skia/src/utils/SkMultiPictureDocument.h" |
| |
| namespace printing { |
| |
| PdfCompositorImpl::PdfCompositorImpl( |
| const std::string& creator, |
| std::unique_ptr<service_manager::ServiceContextRef> service_ref) |
| : service_ref_(std::move(service_ref)), creator_(creator) {} |
| |
| PdfCompositorImpl::~PdfCompositorImpl() = default; |
| |
| void PdfCompositorImpl::AddSubframeContent( |
| uint64_t frame_guid, |
| mojo::ScopedSharedBufferHandle serialized_content, |
| const ContentToFrameMap& subframe_content_map) { |
| // Add this frame and its serialized content. |
| DCHECK(!base::ContainsKey(frame_info_map_, frame_guid)); |
| frame_info_map_[frame_guid] = std::make_unique<FrameInfo>( |
| GetShmFromMojoHandle(std::move(serialized_content)), |
| subframe_content_map); |
| |
| // If the request is not here yet, we do nothing more. |
| // Otherwise, we need to check whether the request actually waits on this |
| // frame content. |
| if (!request_) |
| return; |
| |
| // Get the pending list which is a list of subframes this frame needs |
| // but still unavailable. |
| std::vector<uint64_t> pending_subframes; |
| for (auto& subframe_content : subframe_content_map) { |
| auto subframe_guid = subframe_content.second; |
| if (!base::ContainsKey(frame_info_map_, subframe_guid)) |
| pending_subframes.push_back(subframe_guid); |
| } |
| |
| // Check for the request's pending list. |
| // If the request needs the this frame, we can remove the dependecy, but |
| // update with this frame's pending list. |
| auto& pending_list = request_->pending_subframes; |
| if (!pending_list.erase(frame_guid)) { |
| // The request doesn't directly waits on this frame, simply return. |
| return; |
| } |
| std::copy(pending_subframes.begin(), pending_subframes.end(), |
| std::inserter(pending_list, pending_list.end())); |
| if (pending_list.empty()) { |
| // If the request doesn't wait on any subframes which means it is ready, |
| // fullfill the request now. |
| auto iter = frame_info_map_.find(request_->frame_guid); |
| DCHECK(iter != frame_info_map_.end()); |
| auto& frame_info = iter->second; |
| FullfillRequest(request_->frame_guid, request_->page_number, |
| std::move(frame_info->serialized_content), |
| frame_info->subframe_content_map, |
| std::move(request_->callback)); |
| } |
| } |
| |
| void PdfCompositorImpl::CompositePageToPdf( |
| uint64_t frame_guid, |
| uint32_t page_num, |
| mojo::ScopedSharedBufferHandle serialized_content, |
| const ContentToFrameMap& subframe_content_map, |
| mojom::PdfCompositor::CompositePageToPdfCallback callback) { |
| HandleCompositionRequest(frame_guid, page_num, std::move(serialized_content), |
| subframe_content_map, std::move(callback)); |
| } |
| |
| void PdfCompositorImpl::CompositeDocumentToPdf( |
| uint64_t frame_guid, |
| mojo::ScopedSharedBufferHandle serialized_content, |
| const ContentToFrameMap& subframe_content_map, |
| mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) { |
| HandleCompositionRequest(frame_guid, base::nullopt, |
| std::move(serialized_content), subframe_content_map, |
| std::move(callback)); |
| } |
| |
| bool PdfCompositorImpl::IsReadyToComposite( |
| uint64_t frame_guid, |
| const ContentToFrameMap& subframe_content_map, |
| base::flat_set<uint64_t>* pending_subframes) { |
| pending_subframes->clear(); |
| base::flat_set<uint64_t> visited_frames; |
| visited_frames.insert(frame_guid); |
| CheckFramesForReadiness(frame_guid, subframe_content_map, pending_subframes, |
| &visited_frames); |
| return pending_subframes->empty(); |
| } |
| |
| void PdfCompositorImpl::CheckFramesForReadiness( |
| uint64_t frame_guid, |
| const ContentToFrameMap& subframe_content_map, |
| base::flat_set<uint64_t>* pending_subframes, |
| base::flat_set<uint64_t>* visited) { |
| for (auto& subframe_content : subframe_content_map) { |
| auto subframe_guid = subframe_content.second; |
| // If this frame has been checked, skip it. |
| auto result = visited->insert(subframe_guid); |
| if (!result.second) |
| continue; |
| |
| auto iter = frame_info_map_.find(subframe_guid); |
| if (iter == frame_info_map_.end()) { |
| pending_subframes->insert(subframe_guid); |
| } else { |
| CheckFramesForReadiness(subframe_guid, iter->second->subframe_content_map, |
| pending_subframes, visited); |
| } |
| } |
| } |
| |
| void PdfCompositorImpl::HandleCompositionRequest( |
| uint64_t frame_guid, |
| base::Optional<uint32_t> page_num, |
| mojo::ScopedSharedBufferHandle serialized_content, |
| const ContentToFrameMap& subframe_content_map, |
| CompositeToPdfCallback callback) { |
| base::flat_set<uint64_t> pending_subframes; |
| if (IsReadyToComposite(frame_guid, subframe_content_map, |
| &pending_subframes)) { |
| FullfillRequest(frame_guid, page_num, |
| GetShmFromMojoHandle(std::move(serialized_content)), |
| subframe_content_map, std::move(callback)); |
| return; |
| } |
| |
| // When it is not ready yet, keep its information and |
| // wait until all dependent subframes are ready. |
| DCHECK(!base::ContainsKey(frame_info_map_, frame_guid)); |
| frame_info_map_[frame_guid] = std::make_unique<FrameInfo>( |
| GetShmFromMojoHandle(std::move(serialized_content)), |
| subframe_content_map); |
| |
| request_ = std::make_unique<RequestInfo>( |
| frame_guid, page_num, std::move(pending_subframes), std::move(callback)); |
| } |
| |
| mojom::PdfCompositor::Status PdfCompositorImpl::CompositeToPdf( |
| uint64_t frame_guid, |
| base::Optional<uint32_t> page_num, |
| std::unique_ptr<base::SharedMemory> shared_mem, |
| const ContentToFrameMap& subframe_content_map, |
| mojo::ScopedSharedBufferHandle* handle) { |
| DeserializationContext subframes = |
| GetDeserializationContext(subframe_content_map); |
| |
| // Read in content and convert it into pdf. |
| SkMemoryStream stream(shared_mem->memory(), shared_mem->mapped_size()); |
| int page_count = SkMultiPictureDocumentReadPageCount(&stream); |
| if (!page_count) { |
| DLOG(ERROR) << "CompositeToPdf: No page is read."; |
| return mojom::PdfCompositor::Status::CONTENT_FORMAT_ERROR; |
| } |
| |
| std::vector<SkDocumentPage> pages(page_count); |
| // TODO(weili): Change the default functions to actual implementation. |
| SkDeserialProcs procs; |
| if (!SkMultiPictureDocumentRead(&stream, pages.data(), page_count, &procs)) { |
| DLOG(ERROR) << "CompositeToPdf: Page reading failed."; |
| return mojom::PdfCompositor::Status::CONTENT_FORMAT_ERROR; |
| } |
| |
| SkDynamicMemoryWStream wstream; |
| sk_sp<SkDocument> doc = MakePdfDocument(creator_, &wstream); |
| |
| for (const auto& page : pages) { |
| SkCanvas* canvas = doc->beginPage(page.fSize.width(), page.fSize.height()); |
| canvas->drawPicture(page.fPicture); |
| doc->endPage(); |
| } |
| doc->close(); |
| |
| *handle = mojo::SharedBufferHandle::Create(wstream.bytesWritten()); |
| DCHECK((*handle).is_valid()); |
| |
| mojo::ScopedSharedBufferMapping mapping = |
| (*handle)->Map(wstream.bytesWritten()); |
| DCHECK(mapping); |
| wstream.copyToAndReset(mapping.get()); |
| |
| return mojom::PdfCompositor::Status::SUCCESS; |
| } |
| |
| sk_sp<SkPicture> PdfCompositorImpl::CompositeSubframe(uint64_t frame_guid) { |
| // The content of this frame should be available. |
| auto iter = frame_info_map_.find(frame_guid); |
| DCHECK(iter != frame_info_map_.end()); |
| std::unique_ptr<FrameInfo>& frame_info = iter->second; |
| frame_info->composited = true; |
| |
| // Composite subframes first. |
| DeserializationContext subframes = |
| GetDeserializationContext(frame_info->subframe_content_map); |
| |
| // Composite the entire frame. |
| SkMemoryStream stream(iter->second->serialized_content->memory(), |
| iter->second->serialized_content->mapped_size()); |
| // TODO(weili): Change the default functions to actual implementation. |
| SkDeserialProcs procs; |
| iter->second->content = SkPicture::MakeFromStream(&stream, &procs); |
| return iter->second->content; |
| } |
| |
| PdfCompositorImpl::DeserializationContext |
| PdfCompositorImpl::GetDeserializationContext( |
| const ContentToFrameMap& subframe_content_map) { |
| DeserializationContext subframes; |
| for (auto& content_info : subframe_content_map) { |
| uint64_t frame_guid = content_info.first; |
| uint32_t content_id = content_info.second; |
| auto frame_iter = frame_info_map_.find(frame_guid); |
| if (frame_iter == frame_info_map_.end()) |
| continue; |
| |
| if (frame_iter->second->composited) |
| subframes[content_id] = frame_iter->second->content; |
| else |
| subframes[content_id] = CompositeSubframe(frame_iter->first); |
| } |
| return subframes; |
| } |
| |
| void PdfCompositorImpl::FullfillRequest( |
| uint64_t frame_guid, |
| base::Optional<uint32_t> page_num, |
| std::unique_ptr<base::SharedMemory> serialized_content, |
| const ContentToFrameMap& subframe_content_map, |
| CompositeToPdfCallback callback) { |
| mojo::ScopedSharedBufferHandle handle; |
| auto status = |
| CompositeToPdf(frame_guid, page_num, std::move(serialized_content), |
| subframe_content_map, &handle); |
| std::move(callback).Run(status, std::move(handle)); |
| } |
| |
| PdfCompositorImpl::FrameInfo::FrameInfo( |
| std::unique_ptr<base::SharedMemory> content, |
| const ContentToFrameMap& map) |
| : serialized_content(std::move(content)), subframe_content_map(map) {} |
| |
| PdfCompositorImpl::FrameInfo::~FrameInfo() {} |
| |
| PdfCompositorImpl::RequestInfo::RequestInfo( |
| uint64_t frame_guid, |
| base::Optional<uint32_t> page_num, |
| const base::flat_set<uint64_t>& pending_subframes, |
| mojom::PdfCompositor::CompositePageToPdfCallback callback) |
| : frame_guid(frame_guid), |
| page_number(page_num), |
| pending_subframes(pending_subframes), |
| callback(std::move(callback)) {} |
| |
| PdfCompositorImpl::RequestInfo::~RequestInfo() {} |
| |
| } // namespace printing |