blob: 49ae083067fa60e7a420c9caace9986bdc6a7122 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/printing/common/cloud_print_cdd_conversion.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/cloud_devices/common/cloud_device_description.h"
#include "components/cloud_devices/common/printer_description.h"
#include "printing/backend/print_backend.h"
#include "printing/mojom/print.mojom.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "printing/printing_features.h"
#endif // BUILDFLAG(IS_CHROMEOS)
namespace printer = cloud_devices::printer;
namespace cloud_print {
namespace {
#if BUILDFLAG(IS_WIN)
constexpr char kIdPageOutputQuality[] = "page_output_quality";
constexpr char kDisplayNamePageOutputQuality[] = "Page output quality";
#endif // BUILDFLAG(IS_WIN)
printer::DuplexType ToCloudDuplexType(printing::mojom::DuplexMode mode) {
switch (mode) {
case printing::mojom::DuplexMode::kSimplex:
return printer::DuplexType::NO_DUPLEX;
case printing::mojom::DuplexMode::kLongEdge:
return printer::DuplexType::LONG_EDGE;
case printing::mojom::DuplexMode::kShortEdge:
return printer::DuplexType::SHORT_EDGE;
default:
NOTREACHED();
}
}
#if BUILDFLAG(IS_CHROMEOS)
printer::TypedValueVendorCapability::ValueType ToCloudValueType(
printing::AdvancedCapability::Type type) {
switch (type) {
case printing::AdvancedCapability::Type::kBoolean:
return printer::TypedValueVendorCapability::ValueType::BOOLEAN;
case printing::AdvancedCapability::Type::kFloat:
return printer::TypedValueVendorCapability::ValueType::FLOAT;
case printing::AdvancedCapability::Type::kInteger:
return printer::TypedValueVendorCapability::ValueType::INTEGER;
case printing::AdvancedCapability::Type::kString:
return printer::TypedValueVendorCapability::ValueType::STRING;
default:
NOTREACHED();
}
}
#endif // BUILDFLAG(IS_CHROMEOS)
printer::Media ConvertPaperToMedia(
const printing::PrinterSemanticCapsAndDefaults::Paper& paper) {
if (paper.SupportsCustomSize()) {
return printer::MediaBuilder()
.WithCustomName(paper.display_name(), paper.vendor_id())
.WithSizeAndDefaultPrintableArea(paper.size_um())
.WithMaxHeight(paper.max_height_um())
.Build();
}
gfx::Size paper_size = paper.size_um();
gfx::Rect paper_printable_area = paper.printable_area_um();
// When converting to Media, the size and printable area should have a larger
// height than width.
if (paper_size.width() > paper_size.height()) {
// When swapping the printable_area, we can't simply transpose the rect
// since the margins may not be the same on all sides. A visualization may
// help. Suppose we have a page with width of 127000 and height of 76200.
// Additionally, the left margin is 1000, right margin is 700, bottom margin
// is 500, and top margin is 200.
//
// +---------- 127000 ---------+
// | 200 |
// 76200 +-----------+ |
// | | | |
// | 1000 | | 700 |
// | | | |
// | +-----------+ |
// | 500 |
// +---------------------------+
//
// After swapping the page size and printable area (rotating 90 degrees
// clockwise), this should be the resulting sizes:
//
// +---- 76200 ---+
// | |
// | 1000 |
// | 127000
// | +-----+ |
// | | | |
// | | | |
// |500 | |200|
// | | | |
// | | | |
// | +-----+ |
// | |
// | 700 |
// +--------------+
//
// Namely, the new x value for the printable area is: the old printable area
// y value. The new y value for the printable area is: (the old width) -
// (the old printable area width) - (the old printable areay x value). Note
// that if the top/bottom margins are equal and the left/right margins are
// equal, then a simple transpose does indeed work.
// Rotate clockwise by 90 degrees.
int new_x = paper_printable_area.y();
int new_y = paper_size.width() - paper_printable_area.width() -
paper_printable_area.x();
paper_printable_area.SetRect(new_x, new_y, paper_printable_area.height(),
paper_printable_area.width());
paper_size.SetSize(paper_size.height(), paper_size.width());
}
return printer::MediaBuilder()
.WithSizeAndPrintableArea(paper_size, paper_printable_area)
.WithNameMaybeBasedOnSize(paper.display_name(), paper.vendor_id())
.WithBorderlessVariant(paper.has_borderless_variant())
.Build();
}
printer::MediaCapability GetMediaCapabilities(
const printing::PrinterSemanticCapsAndDefaults& semantic_info) {
printer::MediaCapability media_capabilities;
bool is_default_set = false;
const printing::PrinterSemanticCapsAndDefaults::Paper& default_paper =
semantic_info.default_paper;
printer::Media default_media =
printer::MediaBuilder()
.WithSizeAndPrintableArea(default_paper.size_um(),
default_paper.printable_area_um())
.WithNameMaybeBasedOnSize(default_paper.display_name(),
default_paper.vendor_id())
.WithBorderlessVariant(default_paper.has_borderless_variant())
.Build();
for (const auto& paper : semantic_info.papers) {
printer::Media new_media = ConvertPaperToMedia(paper);
if (!new_media.IsValid())
continue;
if (media_capabilities.Contains(new_media))
continue;
if (!default_media.IsValid())
default_media = new_media;
media_capabilities.AddDefaultOption(new_media, new_media == default_media);
is_default_set = is_default_set || (new_media == default_media);
}
if (!is_default_set && default_media.IsValid())
media_capabilities.AddDefaultOption(default_media, true);
// Allow user defined paper sizes to be repeats of existing paper sizes.
// Do not allow user defined paper sizes to be the default, for now.
// TODO(thestig): Figure out the default paper policy here.
for (const auto& paper : semantic_info.user_defined_papers) {
printer::Media new_media = ConvertPaperToMedia(paper);
if (!new_media.IsValid())
continue;
media_capabilities.AddOption(new_media);
}
return media_capabilities;
}
printer::MediaTypeCapability GetMediaTypeCapabilities(
const printing::PrinterSemanticCapsAndDefaults& semantic_info) {
printer::MediaTypeCapability media_type_capabilities;
for (const auto& media_type : semantic_info.media_types) {
printer::MediaType new_media_type(media_type.vendor_id,
media_type.display_name);
if (!new_media_type.IsValid()) {
continue;
}
if (media_type_capabilities.Contains(new_media_type)) {
continue;
}
media_type_capabilities.AddDefaultOption(
new_media_type,
media_type.vendor_id == semantic_info.default_media_type.vendor_id);
}
return media_type_capabilities;
}
printer::DpiCapability GetDpiCapabilities(
const printing::PrinterSemanticCapsAndDefaults& semantic_info) {
printer::DpiCapability dpi_capabilities;
bool is_default_set = false;
printer::Dpi default_dpi(semantic_info.default_dpi.width(),
semantic_info.default_dpi.height());
for (const auto& dpi : semantic_info.dpis) {
printer::Dpi new_dpi(dpi.width(), dpi.height());
if (!new_dpi.IsValid())
continue;
if (dpi_capabilities.Contains(new_dpi))
continue;
if (!default_dpi.IsValid())
default_dpi = new_dpi;
dpi_capabilities.AddDefaultOption(new_dpi, new_dpi == default_dpi);
is_default_set = is_default_set || (new_dpi == default_dpi);
}
if (!is_default_set && default_dpi.IsValid())
dpi_capabilities.AddDefaultOption(default_dpi, true);
return dpi_capabilities;
}
#if BUILDFLAG(IS_CHROMEOS)
printer::VendorCapabilities GetVendorCapabilities(
const printing::PrinterSemanticCapsAndDefaults& semantic_info) {
printer::VendorCapabilities vendor_capabilities;
for (const auto& capability : semantic_info.advanced_capabilities) {
std::string capability_name = capability.display_name.empty()
? capability.name
: capability.display_name;
if (capability.values.empty()) {
vendor_capabilities.AddOption(
printer::VendorCapability(capability.name, capability_name,
printer::TypedValueVendorCapability(
ToCloudValueType(capability.type))));
continue;
}
printer::SelectVendorCapability select_capability;
for (const auto& value : capability.values) {
std::string localized_value =
value.display_name.empty() ? value.name : value.display_name;
select_capability.AddDefaultOption(
printer::SelectVendorCapabilityOption(value.name, localized_value),
value.name == capability.default_value);
}
vendor_capabilities.AddOption(printer::VendorCapability(
capability.name, capability_name, std::move(select_capability)));
}
return vendor_capabilities;
}
printer::FitToPageCapability GetFitToPageCapabilities(
const printing::PrinterSemanticCapsAndDefaults& semantic_info) {
auto ToFitToPageType = [](const printing::mojom::PrintScalingType& type) {
switch (type) {
case printing::mojom::PrintScalingType::kAuto:
return printer::FitToPageType::AUTO;
case printing::mojom::PrintScalingType::kAutoFit:
return printer::FitToPageType::AUTO_FIT;
case printing::mojom::PrintScalingType::kFit:
return printer::FitToPageType::FIT;
case printing::mojom::PrintScalingType::kFill:
return printer::FitToPageType::FILL;
case printing::mojom::PrintScalingType::kNone:
return printer::FitToPageType::NONE;
case printing::mojom::PrintScalingType::kUnknownPrintScalingType:
NOTREACHED();
}
};
printer::FitToPageCapability fit_to_page;
for (const auto& value : semantic_info.print_scaling_types) {
if (value == printing::mojom::PrintScalingType::kUnknownPrintScalingType) {
continue;
}
fit_to_page.AddOption(ToFitToPageType(value));
}
if (semantic_info.print_scaling_type_default !=
printing::mojom::PrintScalingType::kUnknownPrintScalingType) {
auto default_type =
ToFitToPageType(semantic_info.print_scaling_type_default);
// If default value is not among supported options, return empty options.
if (!fit_to_page.Contains(default_type)) {
return {};
}
fit_to_page.AddDefaultOption(default_type, true);
} else if (!fit_to_page.empty()) {
fit_to_page.AddDefaultOption(fit_to_page[0], true);
}
return fit_to_page;
}
// Helper that verifies if the margins are unique and stores them to the
// supplied vector.
void StoreMarginsUnique(const printing::PaperMargins& margins_um,
std::vector<printer::Margins>& margins_storage) {
printer::Margins printer_margins(
margins_um.top_margin_um, margins_um.right_margin_um,
margins_um.bottom_margin_um, margins_um.left_margin_um);
if (!base::Contains(margins_storage, printer_margins)) {
margins_storage.emplace_back(std::move(printer_margins));
}
}
printer::MarginsCapability GetMarginsCapabilities(
const printing::PrinterSemanticCapsAndDefaults& semantic_info) {
std::optional<printer::Margins> default_margins = std::nullopt;
if (semantic_info.default_paper.supported_margins_um().has_value()) {
const auto& margins =
semantic_info.default_paper.supported_margins_um().value();
default_margins =
printer::Margins(margins.top_margin_um, margins.right_margin_um,
margins.bottom_margin_um, margins.left_margin_um);
}
// Populate the `all_papers` with all the available papers.
printing::PrinterSemanticCapsAndDefaults::Papers all_papers;
all_papers.reserve(semantic_info.papers.size() +
semantic_info.user_defined_papers.size());
all_papers.insert(all_papers.end(), semantic_info.papers.begin(),
semantic_info.papers.end());
all_papers.insert(all_papers.end(), semantic_info.user_defined_papers.begin(),
semantic_info.user_defined_papers.end());
// Stores all the available sets of margins from papers. This temporary
// container is used to avoid duplicates, which is then populated to the
// `margins_capabilites`.
std::vector<printer::Margins> available_margins_um;
for (const auto& paper : all_papers) {
if (!paper.supported_margins_um().has_value()) {
continue;
}
StoreMarginsUnique(paper.supported_margins_um().value(),
available_margins_um);
if (paper.has_borderless_variant()) {
StoreMarginsUnique(printing::PaperMargins(), available_margins_um);
}
}
// Populate the `margins_capabilities` with the unique margins.
printer::MarginsCapability margins_capabilities;
bool default_margins_set = false;
for (const auto& margins : available_margins_um) {
if (!default_margins.has_value()) {
default_margins = margins;
}
margins_capabilities.AddDefaultOption(margins, default_margins == margins);
default_margins_set = default_margins_set || (default_margins == margins);
}
if (!default_margins_set && default_margins.has_value()) {
margins_capabilities.AddDefaultOption(default_margins.value(), true);
}
return margins_capabilities;
}
#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_WIN)
printer::SelectVendorCapability GetPageOutputQualityCapabilities(
const printing::PrinterSemanticCapsAndDefaults& semantic_info) {
printer::SelectVendorCapability page_output_quality_capabilities;
const std::optional<printing::PageOutputQuality>& page_output_quality =
semantic_info.page_output_quality;
for (const auto& attribute : page_output_quality->qualities) {
page_output_quality_capabilities.AddDefaultOption(
printer::SelectVendorCapabilityOption(attribute.name,
attribute.display_name),
attribute.name == page_output_quality->default_quality);
}
return page_output_quality_capabilities;
}
#endif // BUILDFLAG(IS_WIN)
} // namespace
base::Value PrinterSemanticCapsAndDefaultsToCdd(
const printing::PrinterSemanticCapsAndDefaults& semantic_info) {
cloud_devices::CloudDeviceDescription description;
printer::ContentTypesCapability content_types;
content_types.AddOption("application/pdf");
content_types.SaveTo(&description);
if (semantic_info.collate_capable) {
printer::CollateCapability collate;
collate.set_default_value(semantic_info.collate_default);
collate.SaveTo(&description);
}
printer::Copies copies_val;
copies_val.max_value = semantic_info.copies_max;
printer::CopiesCapability copies_cap;
copies_cap.set_value(copies_val);
copies_cap.SaveTo(&description);
if (semantic_info.duplex_modes.size() > 1) {
printer::DuplexCapability duplex;
for (printing::mojom::DuplexMode mode : semantic_info.duplex_modes) {
duplex.AddDefaultOption(ToCloudDuplexType(mode),
semantic_info.duplex_default == mode);
}
duplex.SaveTo(&description);
}
printer::ColorCapability color;
if (semantic_info.color_default || semantic_info.color_changeable) {
printer::Color standard_color(printer::ColorType::STANDARD_COLOR);
standard_color.vendor_id =
base::NumberToString(static_cast<int>(semantic_info.color_model));
color.AddDefaultOption(standard_color, semantic_info.color_default);
}
if (!semantic_info.color_default || semantic_info.color_changeable) {
printer::Color standard_monochrome(printer::ColorType::STANDARD_MONOCHROME);
standard_monochrome.vendor_id =
base::NumberToString(static_cast<int>(semantic_info.bw_model));
color.AddDefaultOption(standard_monochrome, !semantic_info.color_default);
}
color.SaveTo(&description);
if (!semantic_info.papers.empty()) {
printer::MediaCapability media = GetMediaCapabilities(semantic_info);
DCHECK(media.IsValid());
media.SaveTo(&description);
}
// Only create this capability if more than one media type is supported.
if (semantic_info.media_types.size() > 1) {
printer::MediaTypeCapability media_type =
GetMediaTypeCapabilities(semantic_info);
DCHECK(media_type.IsValid());
media_type.SaveTo(&description);
}
if (!semantic_info.dpis.empty()) {
printer::DpiCapability dpi = GetDpiCapabilities(semantic_info);
DCHECK(dpi.IsValid());
dpi.SaveTo(&description);
}
printer::OrientationCapability orientation;
orientation.AddDefaultOption(printer::OrientationType::PORTRAIT, true);
orientation.AddOption(printer::OrientationType::LANDSCAPE);
orientation.AddOption(printer::OrientationType::AUTO_ORIENTATION);
orientation.SaveTo(&description);
#if BUILDFLAG(IS_CHROMEOS)
printer::PinCapability pin;
pin.set_value(semantic_info.pin_supported);
pin.SaveTo(&description);
if (!semantic_info.advanced_capabilities.empty()) {
printer::VendorCapabilities vendor_capabilities =
GetVendorCapabilities(semantic_info);
vendor_capabilities.SaveTo(&description);
}
if (base::FeatureList::IsEnabled(
printing::features::kApiPrintingMarginsAndScale)) {
if (!semantic_info.print_scaling_types.empty()) {
printer::FitToPageCapability fit_to_page =
GetFitToPageCapabilities(semantic_info);
if (fit_to_page.IsValid()) {
fit_to_page.SaveTo(&description);
}
}
if (!semantic_info.papers.empty()) {
printer::MarginsCapability margins =
GetMarginsCapabilities(semantic_info);
if (margins.IsValid()) {
margins.SaveTo(&description);
}
}
}
#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_WIN)
if (semantic_info.page_output_quality) {
printer::VendorCapabilities vendor_capabilities;
vendor_capabilities.AddOption(printer::VendorCapability(
kIdPageOutputQuality, kDisplayNamePageOutputQuality,
GetPageOutputQualityCapabilities(semantic_info)));
vendor_capabilities.SaveTo(&description);
}
#endif // BUILDFLAG(IS_WIN)
return std::move(description).ToValue();
}
} // namespace cloud_print