blob: c140bf4ed222e668081024dab189a5baee6f4ff0 [file] [log] [blame]
// 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::DuplexType::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::ColorType::STANDARD_MONOCHROME:
case cloud_devices::printer::ColorType::CUSTOM_MONOCHROME:
use_color = false;
break;
case cloud_devices::printer::ColorType::STANDARD_COLOR:
case cloud_devices::printer::ColorType::CUSTOM_COLOR:
case cloud_devices::printer::ColorType::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::DuplexType::NO_DUPLEX:
result.duplex_mode = DuplexMode::SIMPLEX;
result.odd_page_transform = TRANSFORM_NORMAL;
break;
case cloud_devices::printer::DuplexType::LONG_EDGE:
if (document_sheet_back ==
cloud_devices::printer::DocumentSheetBack::ROTATED) {
result.odd_page_transform = TRANSFORM_ROTATE_180;
} else if (document_sheet_back ==
cloud_devices::printer::DocumentSheetBack::FLIPPED) {
result.odd_page_transform = TRANSFORM_FLIP_VERTICAL;
}
break;
case cloud_devices::printer::DuplexType::SHORT_EDGE:
result.duplex_mode = DuplexMode::SHORT_EDGE;
if (document_sheet_back ==
cloud_devices::printer::DocumentSheetBack::MANUAL_TUMBLE) {
result.odd_page_transform = TRANSFORM_ROTATE_180;
} else if (document_sheet_back ==
cloud_devices::printer::DocumentSheetBack::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::PwgDocumentTypeSupported::SGRAY_8);
return result;
}
} // namespace printing