| // 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 <algorithm> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/cancelable_callback.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "chrome/services/printing/public/mojom/constants.mojom.h" |
| #include "chrome/services/printing/public/mojom/pdf_to_pwg_raster_converter.mojom.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/common/service_manager_connection.h" |
| #include "mojo/public/cpp/system/platform_handle.h" |
| #include "printing/pdf_render_settings.h" |
| #include "printing/pwg_raster_settings.h" |
| #include "printing/units.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| namespace printing { |
| |
| namespace { |
| |
| using content::BrowserThread; |
| |
| // Converts PDF into PWG raster. Class lives on the UI thread. |
| class PwgRasterConverterHelper |
| : public base::RefCounted<PwgRasterConverterHelper> { |
| public: |
| PwgRasterConverterHelper(const PdfRenderSettings& settings, |
| const PwgRasterSettings& bitmap_settings); |
| |
| void Convert(const base::RefCountedMemory* data, |
| PwgRasterConverter::ResultCallback callback); |
| |
| private: |
| friend class base::RefCounted<PwgRasterConverterHelper>; |
| |
| ~PwgRasterConverterHelper(); |
| |
| void RunCallback(base::ReadOnlySharedMemoryRegion region, |
| uint32_t page_count); |
| |
| PdfRenderSettings settings_; |
| PwgRasterSettings bitmap_settings_; |
| mojo::InterfacePtr<printing::mojom::PdfToPwgRasterConverter> |
| pdf_to_pwg_raster_converter_ptr_; |
| PwgRasterConverter::ResultCallback callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PwgRasterConverterHelper); |
| }; |
| |
| PwgRasterConverterHelper::PwgRasterConverterHelper( |
| const PdfRenderSettings& settings, |
| const PwgRasterSettings& bitmap_settings) |
| : settings_(settings), bitmap_settings_(bitmap_settings) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| } |
| |
| PwgRasterConverterHelper::~PwgRasterConverterHelper() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| } |
| |
| void PwgRasterConverterHelper::Convert( |
| const base::RefCountedMemory* data, |
| PwgRasterConverter::ResultCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| callback_ = std::move(callback); |
| |
| content::ServiceManagerConnection::GetForProcess() |
| ->GetConnector() |
| ->BindInterface(printing::mojom::kChromePrintingServiceName, |
| &pdf_to_pwg_raster_converter_ptr_); |
| |
| pdf_to_pwg_raster_converter_ptr_.set_connection_error_handler( |
| base::BindOnce(&PwgRasterConverterHelper::RunCallback, this, |
| base::ReadOnlySharedMemoryRegion(), /*page_count=*/0)); |
| |
| base::MappedReadOnlyRegion memory = |
| base::ReadOnlySharedMemoryRegion::Create(data->size()); |
| if (!memory.IsValid()) { |
| RunCallback(base::ReadOnlySharedMemoryRegion(), /*page_count=*/0); |
| return; |
| } |
| |
| // TODO(thestig): Write |data| into shared memory in the first place, to avoid |
| // this memcpy(). |
| memcpy(memory.mapping.memory(), data->front(), data->size()); |
| pdf_to_pwg_raster_converter_ptr_->Convert( |
| std::move(memory.region), settings_, bitmap_settings_, |
| base::Bind(&PwgRasterConverterHelper::RunCallback, this)); |
| } |
| |
| void PwgRasterConverterHelper::RunCallback( |
| base::ReadOnlySharedMemoryRegion region, |
| uint32_t page_count) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (callback_) { |
| if (region.IsValid() && page_count > 0) { |
| size_t average_page_size_in_kb = region.GetSize() / 1024; |
| average_page_size_in_kb /= page_count; |
| UMA_HISTOGRAM_MEMORY_KB("Printing.ConversionSize.Pwg", |
| average_page_size_in_kb); |
| std::move(callback_).Run(std::move(region)); |
| } else { |
| // TODO(thestig): Consider adding UMA to track failure rates. |
| std::move(callback_).Run(base::ReadOnlySharedMemoryRegion()); |
| } |
| } |
| pdf_to_pwg_raster_converter_ptr_.reset(); |
| } |
| |
| class PwgRasterConverterImpl : public PwgRasterConverter { |
| public: |
| PwgRasterConverterImpl(); |
| ~PwgRasterConverterImpl() override; |
| |
| void Start(const base::RefCountedMemory* data, |
| const PdfRenderSettings& conversion_settings, |
| const PwgRasterSettings& bitmap_settings, |
| ResultCallback callback) override; |
| |
| private: |
| scoped_refptr<PwgRasterConverterHelper> utility_client_; |
| |
| // Cancelable version of PwgRasterConverter::ResultCallback. |
| base::CancelableOnceCallback<void(base::ReadOnlySharedMemoryRegion)> |
| cancelable_callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PwgRasterConverterImpl); |
| }; |
| |
| PwgRasterConverterImpl::PwgRasterConverterImpl() = default; |
| |
| PwgRasterConverterImpl::~PwgRasterConverterImpl() = default; |
| |
| void PwgRasterConverterImpl::Start(const base::RefCountedMemory* data, |
| const PdfRenderSettings& conversion_settings, |
| const PwgRasterSettings& bitmap_settings, |
| ResultCallback callback) { |
| cancelable_callback_.Reset(std::move(callback)); |
| utility_client_ = base::MakeRefCounted<PwgRasterConverterHelper>( |
| conversion_settings, bitmap_settings); |
| utility_client_->Convert(data, cancelable_callback_.callback()); |
| } |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<PwgRasterConverter> PwgRasterConverter::CreateDefault() { |
| return std::make_unique<PwgRasterConverterImpl>(); |
| } |
| |
| // static |
| PdfRenderSettings PwgRasterConverter::GetConversionSettings( |
| const cloud_devices::CloudDeviceDescription& printer_capabilities, |
| const gfx::Size& page_size, |
| bool use_color) { |
| gfx::Size dpi = gfx::Size(kDefaultPdfDpi, kDefaultPdfDpi); |
| cloud_devices::printer::DpiCapability dpis; |
| if (dpis.LoadFrom(printer_capabilities)) |
| dpi = gfx::Size(dpis.GetDefault().horizontal, dpis.GetDefault().vertical); |
| |
| bool page_is_landscape = |
| static_cast<double>(page_size.width()) / dpi.width() > |
| static_cast<double>(page_size.height()) / dpi.height(); |
| |
| // Pdfium assumes that page width is given in dpi.width(), and height in |
| // dpi.height(). If we rotate the page, we need to also swap the DPIs. |
| gfx::Size final_page_size = page_size; |
| if (page_is_landscape) { |
| final_page_size = gfx::Size(page_size.height(), page_size.width()); |
| dpi = gfx::Size(dpi.height(), dpi.width()); |
| } |
| double scale_x = static_cast<double>(dpi.width()) / kPointsPerInch; |
| double scale_y = static_cast<double>(dpi.height()) / kPointsPerInch; |
| |
| // Make vertical rectangle to optimize streaming to printer. Fix orientation |
| // by autorotate. |
| gfx::Rect area(final_page_size.width() * scale_x, |
| final_page_size.height() * scale_y); |
| return PdfRenderSettings(area, gfx::Point(0, 0), dpi, |
| /*autorotate=*/true, use_color, |
| PdfRenderSettings::Mode::NORMAL); |
| } |
| |
| // static |
| PwgRasterSettings PwgRasterConverter::GetBitmapSettings( |
| const cloud_devices::CloudDeviceDescription& printer_capabilities, |
| const cloud_devices::CloudDeviceDescription& ticket) { |
| cloud_devices::printer::DuplexTicketItem duplex_item; |
| cloud_devices::printer::DuplexType duplex_value = |
| cloud_devices::printer::NO_DUPLEX; |
| if (duplex_item.LoadFrom(ticket)) |
| duplex_value = duplex_item.value(); |
| |
| // This assumes |ticket| contains a color ticket item. In case it does not, or |
| // the color is invalid, |color_value| will default to AUTO_COLOR, which works |
| // just fine. With AUTO_COLOR, it may be possible to better determine the |
| // value for |use_color| based on |printer_capabilities|, rather than just |
| // defaulting to the safe value of true. Parsing |printer_capabilities| |
| // requires work, which this method is avoiding on purpose. |
| cloud_devices::printer::Color color_value; |
| cloud_devices::printer::ColorTicketItem color_item; |
| if (color_item.LoadFrom(ticket) && color_item.IsValid()) |
| color_value = color_item.value(); |
| DCHECK(color_value.IsValid()); |
| bool use_color; |
| switch (color_value.type) { |
| case cloud_devices::printer::STANDARD_MONOCHROME: |
| case cloud_devices::printer::CUSTOM_MONOCHROME: |
| use_color = false; |
| break; |
| |
| case cloud_devices::printer::STANDARD_COLOR: |
| case cloud_devices::printer::CUSTOM_COLOR: |
| case cloud_devices::printer::AUTO_COLOR: |
| use_color = true; |
| break; |
| |
| default: |
| NOTREACHED(); |
| use_color = true; // Still need to initialize |color| or MSVC will warn. |
| break; |
| } |
| |
| 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::DocumentSheetBack document_sheet_back = |
| raster_capability.value().document_sheet_back; |
| |
| PwgRasterSettings result; |
| switch (duplex_value) { |
| case cloud_devices::printer::NO_DUPLEX: |
| result.duplex_mode = DuplexMode::SIMPLEX; |
| result.odd_page_transform = TRANSFORM_NORMAL; |
| break; |
| case cloud_devices::printer::LONG_EDGE: |
| result.duplex_mode = DuplexMode::LONG_EDGE; |
| if (document_sheet_back == cloud_devices::printer::ROTATED) |
| result.odd_page_transform = TRANSFORM_ROTATE_180; |
| else if (document_sheet_back == cloud_devices::printer::FLIPPED) |
| result.odd_page_transform = TRANSFORM_FLIP_VERTICAL; |
| break; |
| case cloud_devices::printer::SHORT_EDGE: |
| result.duplex_mode = DuplexMode::SHORT_EDGE; |
| if (document_sheet_back == cloud_devices::printer::MANUAL_TUMBLE) |
| result.odd_page_transform = TRANSFORM_ROTATE_180; |
| else if (document_sheet_back == cloud_devices::printer::FLIPPED) |
| result.odd_page_transform = TRANSFORM_FLIP_HORIZONTAL; |
| break; |
| } |
| |
| result.rotate_all_pages = raster_capability.value().rotate_all_pages; |
| result.reverse_page_order = raster_capability.value().reverse_order_streaming; |
| |
| // No need to check for SRGB_8 support in |types|. CDD spec says: |
| // "any printer that doesn't support SGRAY_8 must be able to perform |
| // conversion from RGB to grayscale... " |
| const auto& types = raster_capability.value().document_types_supported; |
| result.use_color = |
| use_color || !base::ContainsValue(types, cloud_devices::printer::SGRAY_8); |
| |
| return result; |
| } |
| |
| } // namespace printing |