[go: nahoru, domu]

blob: 9878e558e2788fb13599a419662aef933e16dc52 [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 "printing/print_settings_conversion.h"
#include <stddef.h>
#include <algorithm>
#include <cmath>
#include <memory>
#include <string>
#include <utility>
#include <optional>
#include "base/containers/contains.h"
#include "base/containers/fixed_flat_set.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "printing/buildflags/buildflags.h"
#include "printing/mojom/print.mojom.h"
#include "printing/print_job_constants.h"
#include "printing/print_settings.h"
#include "printing/units.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/crosapi/mojom/local_printer.mojom.h"
#include "print_settings_conversion_chromeos.h"
#endif // BUILDFLAG(IS_CHROMEOS)
namespace printing {
namespace {
// Note: If this code crashes, then the caller has passed in invalid `settings`.
// Fix the caller, instead of trying to avoid the crash here.
PageMargins GetCustomMarginsFromJobSettings(const base::Value::Dict& settings) {
PageMargins margins_in_points;
const base::Value::Dict* custom_margins =
settings.FindDict(kSettingMarginsCustom);
margins_in_points.top = custom_margins->FindInt(kSettingMarginTop).value();
margins_in_points.bottom =
custom_margins->FindInt(kSettingMarginBottom).value();
margins_in_points.left = custom_margins->FindInt(kSettingMarginLeft).value();
margins_in_points.right =
custom_margins->FindInt(kSettingMarginRight).value();
return margins_in_points;
}
void SetMarginsToJobSettings(const std::string& json_path,
const PageMargins& margins,
base::Value::Dict& job_settings) {
base::Value::Dict dict;
dict.Set(kSettingMarginTop, margins.top);
dict.Set(kSettingMarginBottom, margins.bottom);
dict.Set(kSettingMarginLeft, margins.left);
dict.Set(kSettingMarginRight, margins.right);
job_settings.Set(json_path, std::move(dict));
}
void SetSizeToJobSettings(const std::string& json_path,
const gfx::Size& size,
base::Value::Dict& job_settings) {
base::Value::Dict dict;
dict.Set("width", size.width());
dict.Set("height", size.height());
job_settings.Set(json_path, std::move(dict));
}
void SetRectToJobSettings(const std::string& json_path,
const gfx::Rect& rect,
base::Value::Dict& job_settings) {
base::Value::Dict dict;
dict.Set("x", rect.x());
dict.Set("y", rect.y());
dict.Set("width", rect.width());
dict.Set("height", rect.height());
job_settings.Set(json_path, std::move(dict));
}
void SetPrintableAreaIfValid(PrintSettings& settings,
const gfx::Size& size_microns,
const base::Value::Dict& media_size) {
std::optional<int> left_microns =
media_size.FindInt(kSettingsImageableAreaLeftMicrons);
std::optional<int> bottom_microns =
media_size.FindInt(kSettingsImageableAreaBottomMicrons);
std::optional<int> right_microns =
media_size.FindInt(kSettingsImageableAreaRightMicrons);
std::optional<int> top_microns =
media_size.FindInt(kSettingsImageableAreaTopMicrons);
if (!bottom_microns.has_value() || !left_microns.has_value() ||
!right_microns.has_value() || !top_microns.has_value()) {
return;
}
// Scale the page size and printable area to device units.
// Blink doesn't support different dpi settings in X and Y axis. Because of
// this, printers with non-square pixels still scale page size and printable
// area using device_units_per_inch() instead of their respective dimensions
// in device_units_per_inch_size().
float scale =
static_cast<float>(settings.device_units_per_inch()) / kMicronsPerInch;
gfx::Size page_size = gfx::ScaleToRoundedSize(size_microns, scale);
// Flip the y-axis since the imageable area origin is at the bottom-left,
// while the gfx::Rect origin is at the top-left.
gfx::Rect printable_area = gfx::ScaleToRoundedRect(
{left_microns.value(), size_microns.height() - top_microns.value(),
right_microns.value() - left_microns.value(),
top_microns.value() - bottom_microns.value()},
scale);
// Sanity check that the printable area makes sense.
if (printable_area.IsEmpty() ||
!gfx::Rect(page_size).Contains(printable_area)) {
return;
}
settings.SetPrinterPrintableArea(page_size, printable_area,
/*landscape_needs_flip=*/true);
}
} // namespace
PageRanges GetPageRangesFromJobSettings(const base::Value::Dict& job_settings) {
PageRanges page_ranges;
const base::Value::List* page_range_array =
job_settings.FindList(kSettingPageRange);
if (!page_range_array) {
return page_ranges;
}
for (const base::Value& page_range : *page_range_array) {
if (!page_range.is_dict()) {
continue;
}
const auto& dict = page_range.GetDict();
std::optional<int> from = dict.FindInt(kSettingPageRangeFrom);
std::optional<int> to = dict.FindInt(kSettingPageRangeTo);
if (!from.has_value() || !to.has_value()) {
continue;
}
// Page numbers are 1-based in the dictionary.
// Page numbers are 0-based for the printing context.
page_ranges.push_back(PageRange{static_cast<uint32_t>(from.value() - 1),
static_cast<uint32_t>(to.value() - 1)});
}
return page_ranges;
}
std::unique_ptr<PrintSettings> PrintSettingsFromJobSettings(
const base::Value::Dict& job_settings) {
auto settings = std::make_unique<PrintSettings>();
std::optional<bool> display_header_footer =
job_settings.FindBool(kSettingHeaderFooterEnabled);
if (!display_header_footer.has_value()) {
return nullptr;
}
settings->set_display_header_footer(display_header_footer.value());
if (settings->display_header_footer()) {
const std::string* title =
job_settings.FindString(kSettingHeaderFooterTitle);
const std::string* url = job_settings.FindString(kSettingHeaderFooterURL);
if (!title || !url) {
return nullptr;
}
settings->set_title(base::UTF8ToUTF16(*title));
settings->set_url(base::UTF8ToUTF16(*url));
}
std::optional<bool> backgrounds =
job_settings.FindBool(kSettingShouldPrintBackgrounds);
std::optional<bool> selection_only =
job_settings.FindBool(kSettingShouldPrintSelectionOnly);
if (!backgrounds.has_value() || !selection_only.has_value()) {
return nullptr;
}
settings->set_should_print_backgrounds(backgrounds.value());
settings->set_selection_only(selection_only.value());
std::optional<bool> collate = job_settings.FindBool(kSettingCollate);
std::optional<int> copies = job_settings.FindInt(kSettingCopies);
std::optional<int> color = job_settings.FindInt(kSettingColor);
std::optional<int> duplex_mode = job_settings.FindInt(kSettingDuplexMode);
std::optional<bool> landscape = job_settings.FindBool(kSettingLandscape);
const std::string* device_name = job_settings.FindString(kSettingDeviceName);
std::optional<int> scale_factor = job_settings.FindInt(kSettingScaleFactor);
std::optional<bool> rasterize_pdf =
job_settings.FindBool(kSettingRasterizePdf);
std::optional<int> pages_per_sheet =
job_settings.FindInt(kSettingPagesPerSheet);
if (!collate.has_value() || !copies.has_value() || !color.has_value() ||
!duplex_mode.has_value() || !landscape.has_value() || !device_name ||
!scale_factor.has_value() || !rasterize_pdf.has_value() ||
!pages_per_sheet.has_value()) {
return nullptr;
}
settings->set_collate(collate.value());
settings->set_copies(copies.value());
settings->SetOrientation(landscape.value());
settings->set_device_name(base::UTF8ToUTF16(*device_name));
settings->set_duplex_mode(
static_cast<mojom::DuplexMode>(duplex_mode.value()));
settings->set_color(static_cast<mojom::ColorModel>(color.value()));
settings->set_scale_factor(static_cast<double>(scale_factor.value()) / 100.0);
settings->set_rasterize_pdf(rasterize_pdf.value());
settings->set_pages_per_sheet(pages_per_sheet.value());
std::optional<int> dpi_horizontal =
job_settings.FindInt(kSettingDpiHorizontal);
std::optional<int> dpi_vertical = job_settings.FindInt(kSettingDpiVertical);
if (!dpi_horizontal.has_value() || !dpi_vertical.has_value()) {
return nullptr;
}
settings->set_dpi_xy(dpi_horizontal.value(), dpi_vertical.value());
std::optional<int> rasterize_pdf_dpi =
job_settings.FindInt(kSettingRasterizePdfDpi);
if (rasterize_pdf_dpi.has_value()) {
settings->set_rasterize_pdf_dpi(rasterize_pdf_dpi.value());
}
// Set margin type before setting printable area. `SetPrintableAreaIfValid()`
// calls `PrintSettings::SetPrinterPrintableArea()`, which requires the margin
// type to be set first.
mojom::MarginType margin_type = static_cast<mojom::MarginType>(
job_settings.FindInt(kSettingMarginsType)
.value_or(static_cast<int>(mojom::MarginType::kDefaultMargins)));
if (margin_type < mojom::MarginType::kMinValue ||
margin_type > mojom::MarginType::kMaxValue) {
margin_type = mojom::MarginType::kDefaultMargins;
}
settings->set_margin_type(margin_type);
if (margin_type == mojom::MarginType::kCustomMargins) {
settings->SetCustomMargins(GetCustomMarginsFromJobSettings(job_settings));
}
PrintSettings::RequestedMedia requested_media;
const base::Value::Dict* media_size_value =
job_settings.FindDict(kSettingMediaSize);
if (media_size_value) {
std::optional<int> width_microns =
media_size_value->FindInt(kSettingMediaSizeWidthMicrons);
std::optional<int> height_microns =
media_size_value->FindInt(kSettingMediaSizeHeightMicrons);
if (width_microns.has_value() && height_microns.has_value()) {
requested_media.size_microns =
gfx::Size(width_microns.value(), height_microns.value());
SetPrintableAreaIfValid(*settings, requested_media.size_microns,
*media_size_value);
}
const std::string* vendor_id =
media_size_value->FindString(kSettingMediaSizeVendorId);
if (vendor_id && !vendor_id->empty()) {
requested_media.vendor_id = *vendor_id;
}
}
settings->set_requested_media(requested_media);
std::optional<bool> borderless = job_settings.FindBool(kSettingBorderless);
if (borderless.has_value()) {
settings->set_borderless(borderless.value());
}
const std::string* media_type = job_settings.FindString(kSettingMediaType);
if (media_type && !media_type->empty()) {
settings->set_media_type(*media_type);
}
settings->set_ranges(GetPageRangesFromJobSettings(job_settings));
std::optional<bool> is_modifiable =
job_settings.FindBool(kSettingPreviewModifiable);
if (is_modifiable.has_value()) {
settings->set_is_modifiable(is_modifiable.value());
}
#if BUILDFLAG(IS_CHROMEOS) || (BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_CUPS))
const base::Value::Dict* advanced_settings =
job_settings.FindDict(kSettingAdvancedSettings);
if (advanced_settings) {
for (const auto item : *advanced_settings) {
static constexpr auto kNonJobAttributes =
base::MakeFixedFlatSet<base::StringPiece>(
{"printer-info", "printer-make-and-model", "system_driverinfo"});
if (!base::Contains(kNonJobAttributes, item.first)) {
settings->advanced_settings().emplace(item.first, item.second.Clone());
}
}
}
#endif // BUILDFLAG(IS_CHROMEOS) || (BUILDFLAG(IS_LINUX) &&
// BUILDFLAG(USE_CUPS))
#if BUILDFLAG(IS_CHROMEOS)
bool send_user_info =
job_settings.FindBool(kSettingSendUserInfo).value_or(false);
settings->set_send_user_info(send_user_info);
if (send_user_info) {
const std::string* username = job_settings.FindString(kSettingUsername);
if (username) {
settings->set_username(*username);
}
}
const std::string* oauth_token =
job_settings.FindString(kSettingChromeOSAccessOAuthToken);
if (oauth_token) {
settings->set_oauth_token(*oauth_token);
}
const std::string* pin_value = job_settings.FindString(kSettingPinValue);
if (pin_value) {
settings->set_pin_value(*pin_value);
}
const base::Value::List* client_info_list =
job_settings.FindList(kSettingIppClientInfo);
if (client_info_list) {
settings->set_client_infos(
ConvertJobSettingToClientInfo(*client_info_list));
}
settings->set_printer_manually_selected(
job_settings.FindBool(kSettingPrinterManuallySelected).value_or(false));
std::optional<int> reason = job_settings.FindInt(kSettingPrinterStatusReason);
if (reason.has_value()) {
settings->set_printer_status_reason(
static_cast<crosapi::mojom::StatusReason::Reason>(reason.value()));
}
#endif // BUILDFLAG(IS_CHROMEOS)
return settings;
}
base::Value::Dict PrintSettingsToJobSettingsDebug(
const PrintSettings& settings) {
base::Value::Dict job_settings;
job_settings.Set(kSettingHeaderFooterEnabled,
settings.display_header_footer());
job_settings.Set(kSettingHeaderFooterTitle, settings.title());
job_settings.Set(kSettingHeaderFooterURL, settings.url());
job_settings.Set(kSettingShouldPrintBackgrounds,
settings.should_print_backgrounds());
job_settings.Set(kSettingShouldPrintSelectionOnly, settings.selection_only());
job_settings.Set(kSettingMarginsType,
static_cast<int>(settings.margin_type()));
if (!settings.ranges().empty()) {
base::Value::List page_range_array;
for (const auto& range : settings.ranges()) {
base::Value::Dict dict;
dict.Set(kSettingPageRangeFrom, static_cast<int>(range.from + 1));
dict.Set(kSettingPageRangeTo, static_cast<int>(range.to + 1));
page_range_array.Append(std::move(dict));
}
job_settings.Set(kSettingPageRange, std::move(page_range_array));
}
job_settings.Set(kSettingCollate, settings.collate());
job_settings.Set(kSettingCopies, settings.copies());
job_settings.Set(kSettingColor, static_cast<int>(settings.color()));
job_settings.Set(kSettingDuplexMode,
static_cast<int>(settings.duplex_mode()));
job_settings.Set(kSettingLandscape, settings.landscape());
job_settings.Set(kSettingDeviceName, settings.device_name());
job_settings.Set(kSettingDpiHorizontal, settings.dpi_horizontal());
job_settings.Set(kSettingDpiVertical, settings.dpi_vertical());
job_settings.Set(kSettingScaleFactor,
static_cast<int>((settings.scale_factor() * 100.0) + 0.5));
job_settings.Set(kSettingRasterizePdf, settings.rasterize_pdf());
job_settings.Set(kSettingPagesPerSheet, settings.pages_per_sheet());
// Following values are not read form JSON by InitSettings, so do not have
// common public constants. So just serialize in "debug" section.
base::Value::Dict debug;
debug.Set("dpi", settings.dpi());
debug.Set("deviceUnitsPerInch", settings.device_units_per_inch());
debug.Set("support_alpha_blend", settings.should_print_backgrounds());
debug.Set("media_vendor_id", settings.requested_media().vendor_id);
SetSizeToJobSettings("media_size", settings.requested_media().size_microns,
debug);
SetMarginsToJobSettings("requested_custom_margins_in_points",
settings.requested_custom_margins_in_points(), debug);
const PageSetup& page_setup = settings.page_setup_device_units();
SetMarginsToJobSettings("effective_margins", page_setup.effective_margins(),
debug);
SetSizeToJobSettings("physical_size", page_setup.physical_size(), debug);
SetRectToJobSettings("overlay_area", page_setup.overlay_area(), debug);
SetRectToJobSettings("content_area", page_setup.content_area(), debug);
SetRectToJobSettings("printable_area", page_setup.printable_area(), debug);
job_settings.Set("debug", std::move(debug));
#if BUILDFLAG(IS_CHROMEOS)
job_settings.Set(kSettingIppClientInfo,
ConvertClientInfoToJobSetting(settings.client_infos()));
#endif // BUILDFLAG(IS_CHROMEOS)
return job_settings;
}
} // namespace printing