// Copyright 2013 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 "chrome/browser/printing/pwg_raster_converter.h" #include #include #include "base/bind_helpers.h" #include "base/cancelable_callback.h" #include "base/files/file.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/location.h" #include "base/logging.h" #include "base/macros.h" #include "base/single_thread_task_runner.h" #include "chrome/common/chrome_utility_messages.h" #include "chrome/common/chrome_utility_printing_messages.h" #include "chrome/grit/generated_resources.h" #include "components/cloud_devices/common/cloud_device_description.h" #include "components/cloud_devices/common/printer_description.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/child_process_data.h" #include "content/public/browser/utility_process_host.h" #include "content/public/browser/utility_process_host_client.h" #include "printing/pdf_render_settings.h" #include "printing/pwg_raster_settings.h" #include "printing/units.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" namespace printing { namespace { using content::BrowserThread; class FileHandlers { public: FileHandlers() {} ~FileHandlers() { DCHECK_CURRENTLY_ON(BrowserThread::FILE); } void Init(base::RefCountedMemory* data); bool IsValid(); base::FilePath GetPwgPath() const { return temp_dir_.path().AppendASCII("output.pwg"); } base::FilePath GetPdfPath() const { return temp_dir_.path().AppendASCII("input.pdf"); } IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) { DCHECK(pdf_file_.IsValid()); IPC::PlatformFileForTransit transit = IPC::TakeFileHandleForProcess(std::move(pdf_file_), process); return transit; } IPC::PlatformFileForTransit GetPwgForProcess(base::ProcessHandle process) { DCHECK(pwg_file_.IsValid()); IPC::PlatformFileForTransit transit = IPC::TakeFileHandleForProcess(std::move(pwg_file_), process); return transit; } private: base::ScopedTempDir temp_dir_; base::File pdf_file_; base::File pwg_file_; }; void FileHandlers::Init(base::RefCountedMemory* data) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); if (!temp_dir_.CreateUniqueTempDir()) { return; } if (static_cast(data->size()) != base::WriteFile(GetPdfPath(), data->front_as(), data->size())) { return; } // Reopen in read only mode. pdf_file_.Initialize(GetPdfPath(), base::File::FLAG_OPEN | base::File::FLAG_READ); pwg_file_.Initialize(GetPwgPath(), base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); } bool FileHandlers::IsValid() { return pdf_file_.IsValid() && pwg_file_.IsValid(); } // Converts PDF into PWG raster. // Class uses 3 threads: UI, IO and FILE. // Internal workflow is following: // 1. Create instance on the UI thread. (files_, settings_,) // 2. Create file on the FILE thread. // 3. Start utility process and start conversion on the IO thread. // 4. Run result callback on the UI thread. // 5. Instance is destroyed from any thread that has the last reference. // 6. FileHandlers destroyed on the FILE thread. // This step posts |FileHandlers| to be destroyed on the FILE thread. // All these steps work sequentially, so no data should be accessed // simultaneously by several threads. class PwgUtilityProcessHostClient : public content::UtilityProcessHostClient { public: PwgUtilityProcessHostClient( const PdfRenderSettings& settings, const PwgRasterSettings& bitmap_settings); void Convert(base::RefCountedMemory* data, const PWGRasterConverter::ResultCallback& callback); // UtilityProcessHostClient implementation. void OnProcessCrashed(int exit_code) override; bool OnMessageReceived(const IPC::Message& message) override; private: ~PwgUtilityProcessHostClient() override; // Message handlers. void OnProcessStarted(); void OnSucceeded(); void OnFailed(); void RunCallback(bool success); void StartProcessOnIOThread(); void RunCallbackOnUIThread(bool success); void OnFilesReadyOnUIThread(); scoped_ptr files_; PdfRenderSettings settings_; PwgRasterSettings bitmap_settings_; PWGRasterConverter::ResultCallback callback_; base::WeakPtr utility_process_host_; DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient); }; PwgUtilityProcessHostClient::PwgUtilityProcessHostClient( const printing::PdfRenderSettings& settings, const printing::PwgRasterSettings& bitmap_settings) : settings_(settings), bitmap_settings_(bitmap_settings) {} PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() { } void PwgUtilityProcessHostClient::Convert( base::RefCountedMemory* data, const PWGRasterConverter::ResultCallback& callback) { DCHECK_CURRENTLY_ON(BrowserThread::UI); callback_ = callback; CHECK(!files_); files_.reset(new FileHandlers()); BrowserThread::PostTaskAndReply( BrowserThread::FILE, FROM_HERE, base::Bind(&FileHandlers::Init, base::Unretained(files_.get()), make_scoped_refptr(data)), base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this)); } void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) { OnFailed(); } bool PwgUtilityProcessHostClient::OnMessageReceived( const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message) IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) IPC_MESSAGE_HANDLER( ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded) IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed, OnFailed) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void PwgUtilityProcessHostClient::OnProcessStarted() { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!utility_process_host_) { RunCallbackOnUIThread(false); return; } base::ProcessHandle process = utility_process_host_->GetData().handle; utility_process_host_->Send(new ChromeUtilityMsg_RenderPDFPagesToPWGRaster( files_->GetPdfForProcess(process), settings_, bitmap_settings_, files_->GetPwgForProcess(process))); utility_process_host_.reset(); } void PwgUtilityProcessHostClient::OnSucceeded() { DCHECK_CURRENTLY_ON(BrowserThread::IO); RunCallback(true); } void PwgUtilityProcessHostClient::OnFailed() { DCHECK_CURRENTLY_ON(BrowserThread::IO); RunCallback(false); } void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!files_->IsValid()) { RunCallbackOnUIThread(false); return; } BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this)); } void PwgUtilityProcessHostClient::StartProcessOnIOThread() { DCHECK_CURRENTLY_ON(BrowserThread::IO); utility_process_host_ = content::UtilityProcessHost::Create( this, base::MessageLoop::current()->task_runner())->AsWeakPtr(); utility_process_host_->SetName(l10n_util::GetStringUTF16( IDS_UTILITY_PROCESS_PWG_RASTER_CONVERTOR_NAME)); utility_process_host_->Send(new ChromeUtilityMsg_StartupPing); } void PwgUtilityProcessHostClient::RunCallback(bool success) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this, success)); } void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!callback_.is_null()) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(callback_, success, files_->GetPwgPath())); callback_.Reset(); } } class PWGRasterConverterImpl : public PWGRasterConverter { public: PWGRasterConverterImpl(); ~PWGRasterConverterImpl() override; void Start(base::RefCountedMemory* data, const printing::PdfRenderSettings& conversion_settings, const printing::PwgRasterSettings& bitmap_settings, const ResultCallback& callback) override; private: scoped_refptr utility_client_; base::CancelableCallback callback_; DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl); }; PWGRasterConverterImpl::PWGRasterConverterImpl() { } PWGRasterConverterImpl::~PWGRasterConverterImpl() { } void PWGRasterConverterImpl::Start( base::RefCountedMemory* data, const printing::PdfRenderSettings& conversion_settings, const printing::PwgRasterSettings& bitmap_settings, const ResultCallback& callback) { // Rebind cancelable callback to avoid calling callback if // PWGRasterConverterImpl is destroyed. callback_.Reset(callback); utility_client_ = new PwgUtilityProcessHostClient(conversion_settings, bitmap_settings); utility_client_->Convert(data, callback_.callback()); } } // namespace // static scoped_ptr PWGRasterConverter::CreateDefault() { return scoped_ptr(new PWGRasterConverterImpl()); } // static printing::PdfRenderSettings PWGRasterConverter::GetConversionSettings( const cloud_devices::CloudDeviceDescription& printer_capabilities, const gfx::Size& page_size) { int dpi = printing::kDefaultPdfDpi; cloud_devices::printer::DpiCapability dpis; if (dpis.LoadFrom(printer_capabilities)) dpi = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical); double scale = dpi; scale /= printing::kPointsPerInch; // Make vertical rectangle to optimize streaming to printer. Fix orientation // by autorotate. gfx::Rect area(std::min(page_size.width(), page_size.height()) * scale, std::max(page_size.width(), page_size.height()) * scale); return printing::PdfRenderSettings(area, dpi, true /* autorotate */); } // static printing::PwgRasterSettings PWGRasterConverter::GetBitmapSettings( const cloud_devices::CloudDeviceDescription& printer_capabilities, const cloud_devices::CloudDeviceDescription& ticket) { printing::PwgRasterSettings result; cloud_devices::printer::PwgRasterConfigCapability raster_capability; // If the raster capability fails to load, raster_capability will contain // the default value. raster_capability.LoadFrom(printer_capabilities); cloud_devices::printer::DuplexTicketItem duplex_item; cloud_devices::printer::DuplexType duplex_value = cloud_devices::printer::NO_DUPLEX; cloud_devices::printer::DocumentSheetBack document_sheet_back = raster_capability.value().document_sheet_back; if (duplex_item.LoadFrom(ticket)) { duplex_value = duplex_item.value(); } result.odd_page_transform = printing::TRANSFORM_NORMAL; switch (duplex_value) { case cloud_devices::printer::NO_DUPLEX: result.odd_page_transform = printing::TRANSFORM_NORMAL; break; case cloud_devices::printer::LONG_EDGE: if (document_sheet_back == cloud_devices::printer::ROTATED) { result.odd_page_transform = printing::TRANSFORM_ROTATE_180; } else if (document_sheet_back == cloud_devices::printer::FLIPPED) { result.odd_page_transform = printing::TRANSFORM_FLIP_VERTICAL; } break; case cloud_devices::printer::SHORT_EDGE: if (document_sheet_back == cloud_devices::printer::MANUAL_TUMBLE) { result.odd_page_transform = printing::TRANSFORM_ROTATE_180; } else if (document_sheet_back == cloud_devices::printer::FLIPPED) { result.odd_page_transform = printing::TRANSFORM_FLIP_HORIZONTAL; } } result.rotate_all_pages = raster_capability.value().rotate_all_pages; result.reverse_page_order = raster_capability.value().reverse_order_streaming; return result; } } // namespace printing