chromium/ash/webui/scanning/mojom/scanning_type_converters.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/webui/scanning/mojom/scanning_type_converters.h"
#include "ash/webui/scanning/mojom/scanning.mojom.h"

#include <utility>

#include "base/notreached.h"
#include "mojo/public/cpp/bindings/enum_traits.h"
#include "mojo/public/cpp/bindings/struct_traits.h"

namespace mojo {

namespace {

namespace mojo_ipc = ash::scanning::mojom;

using MojomScanResult = ash::scanning::mojom::ScanResult;
using ProtoScanFailureMode = lorgnette::ScanFailureMode;

using MojomColorMode = ash::scanning::mojom::ColorMode;
using ProtoColorMode = lorgnette::ColorMode;

using MojomSourceType = ash::scanning::mojom::SourceType;
using ProtoSourceType = lorgnette::SourceType;

using MojomFileType = ash::scanning::mojom::FileType;
using ProtoImageFormat = lorgnette::ImageFormat;

// The margin allowed when comparing a scannable area dimension to a page size
// dimension. Accounts for differences due to rounding.
constexpr double kMargin = 1;

// POD struct for page size dimensions in mm.
struct PageSize {
  double width;
  double height;
};

// ISO A3: 297 x 420 mm.
constexpr PageSize kIsoA3PageSize = {
    297,
    420,
};

// ISO A4: 210 x 297 mm.
constexpr PageSize kIsoA4PageSize = {
    210,
    297,
};

// ISO B4: 257 x 364 mm.
constexpr PageSize kIsoB4PageSize = {
    257,
    364,
};

// Legal: 215.9 x 355.6 mm.
constexpr PageSize kLegalPageSize = {
    215.9,
    355.6,
};

// NA Letter: 215.9 x 279.4 mm.
constexpr PageSize kNaLetterPageSize = {
    215.9,
    279.4,
};

// Tabloid: 279.4 x 431.8 mm.
constexpr PageSize kTabloidPageSize = {
    279.4,
    431.8,
};

// Returns true if |area| is large enough to support |page_size|.
bool AreaSupportsPageSize(const lorgnette::ScannableArea& area,
                          const PageSize& page_size) {
  return area.width() + kMargin >= page_size.width &&
         area.height() + kMargin >= page_size.height;
}

// Returns the page sizes the given |area| supports.
std::vector<mojo_ipc::PageSize> GetSupportedPageSizes(
    const lorgnette::ScannableArea& area) {
  std::vector<mojo_ipc::PageSize> page_sizes;
  page_sizes.reserve(7);
  page_sizes.push_back(mojo_ipc::PageSize::kMax);
  if (AreaSupportsPageSize(area, kIsoA3PageSize))
    page_sizes.push_back(mojo_ipc::PageSize::kIsoA3);
  if (AreaSupportsPageSize(area, kIsoA4PageSize))
    page_sizes.push_back(mojo_ipc::PageSize::kIsoA4);
  if (AreaSupportsPageSize(area, kIsoB4PageSize))
    page_sizes.push_back(mojo_ipc::PageSize::kIsoB4);
  if (AreaSupportsPageSize(area, kLegalPageSize))
    page_sizes.push_back(mojo_ipc::PageSize::kLegal);
  if (AreaSupportsPageSize(area, kNaLetterPageSize))
    page_sizes.push_back(mojo_ipc::PageSize::kNaLetter);
  if (AreaSupportsPageSize(area, kTabloidPageSize))
    page_sizes.push_back(mojo_ipc::PageSize::kTabloid);

  return page_sizes;
}

// Sets the scan region based on the given |page_size|. If |page_size| is
// PageSize::kMax, the scan resion is left unset, which will cause the scanner
// to scan the entire scannable area.
void SetScanRegion(const mojo_ipc::PageSize page_size,
                   lorgnette::ScanSettings& settings_out) {
  // The default top-left and bottom-right coordinates are (0,0), so only the
  // bottom-right coordinates need to be set.
  lorgnette::ScanRegion region;
  switch (page_size) {
    case mojo_ipc::PageSize::kIsoA3:
      region.set_bottom_right_x(kIsoA3PageSize.width);
      region.set_bottom_right_y(kIsoA3PageSize.height);
      break;
    case mojo_ipc::PageSize::kIsoA4:
      region.set_bottom_right_x(kIsoA4PageSize.width);
      region.set_bottom_right_y(kIsoA4PageSize.height);
      break;
    case mojo_ipc::PageSize::kIsoB4:
      region.set_bottom_right_x(kIsoB4PageSize.width);
      region.set_bottom_right_y(kIsoB4PageSize.height);
      break;
    case mojo_ipc::PageSize::kLegal:
      region.set_bottom_right_x(kLegalPageSize.width);
      region.set_bottom_right_y(kLegalPageSize.height);
      break;
    case mojo_ipc::PageSize::kNaLetter:
      region.set_bottom_right_x(kNaLetterPageSize.width);
      region.set_bottom_right_y(kNaLetterPageSize.height);
      break;
    case mojo_ipc::PageSize::kTabloid:
      region.set_bottom_right_x(kTabloidPageSize.width);
      region.set_bottom_right_y(kTabloidPageSize.height);
      break;
    case mojo_ipc::PageSize::kMax:
      return;
  }

  *settings_out.mutable_scan_region() = std::move(region);
}

}  // namespace

// static
MojomColorMode EnumTraits<MojomColorMode, ProtoColorMode>::ToMojom(
    ProtoColorMode input) {
  switch (input) {
    case ProtoColorMode::MODE_LINEART:
      return MojomColorMode::kBlackAndWhite;
    case ProtoColorMode::MODE_GRAYSCALE:
      return MojomColorMode::kGrayscale;
    case ProtoColorMode::MODE_COLOR:
      return MojomColorMode::kColor;
    case ProtoColorMode::MODE_UNSPECIFIED:
    case ProtoColorMode::ColorMode_INT_MIN_SENTINEL_DO_NOT_USE_:
    case ProtoColorMode::ColorMode_INT_MAX_SENTINEL_DO_NOT_USE_:
      NOTREACHED();
  }
}

// static
bool EnumTraits<MojomColorMode, ProtoColorMode>::FromMojom(
    MojomColorMode input,
    ProtoColorMode* out) {
  switch (input) {
    case MojomColorMode::kBlackAndWhite:
      *out = ProtoColorMode::MODE_LINEART;
      return true;
    case MojomColorMode::kGrayscale:
      *out = ProtoColorMode::MODE_GRAYSCALE;
      return true;
    case MojomColorMode::kColor:
      *out = ProtoColorMode::MODE_COLOR;
      return true;
  }
  NOTREACHED();
}

// static
MojomSourceType EnumTraits<MojomSourceType, ProtoSourceType>::ToMojom(
    ProtoSourceType input) {
  switch (input) {
    case ProtoSourceType::SOURCE_PLATEN:
      return MojomSourceType::kFlatbed;
    case ProtoSourceType::SOURCE_ADF_SIMPLEX:
      return MojomSourceType::kAdfSimplex;
    case ProtoSourceType::SOURCE_ADF_DUPLEX:
      return MojomSourceType::kAdfDuplex;
    case ProtoSourceType::SOURCE_DEFAULT:
      return MojomSourceType::kDefault;
    case ProtoSourceType::SOURCE_UNSPECIFIED:
      return MojomSourceType::kUnknown;
    case ProtoSourceType::SourceType_INT_MIN_SENTINEL_DO_NOT_USE_:
    case ProtoSourceType::SourceType_INT_MAX_SENTINEL_DO_NOT_USE_:
      NOTREACHED();
  }
}

// static
bool EnumTraits<MojomSourceType, ProtoSourceType>::FromMojom(
    MojomSourceType input,
    ProtoSourceType* out) {
  switch (input) {
    case MojomSourceType::kFlatbed:
      *out = ProtoSourceType::SOURCE_PLATEN;
      return true;
    case MojomSourceType::kAdfSimplex:
      *out = ProtoSourceType::SOURCE_ADF_SIMPLEX;
      return true;
    case MojomSourceType::kAdfDuplex:
      *out = ProtoSourceType::SOURCE_ADF_DUPLEX;
      return true;
    case MojomSourceType::kDefault:
      *out = ProtoSourceType::SOURCE_DEFAULT;
      return true;
    case MojomSourceType::kUnknown:
      *out = ProtoSourceType::SOURCE_UNSPECIFIED;
      return true;
  }
  NOTREACHED();
}

// static
MojomFileType EnumTraits<MojomFileType, ProtoImageFormat>::ToMojom(
    ProtoImageFormat input) {
  switch (input) {
    case ProtoImageFormat::IMAGE_FORMAT_PNG:
      return MojomFileType::kPng;
    case ProtoImageFormat::IMAGE_FORMAT_JPEG:
      return MojomFileType::kJpg;
    case ProtoImageFormat::ImageFormat_INT_MIN_SENTINEL_DO_NOT_USE_:
    case ProtoImageFormat::ImageFormat_INT_MAX_SENTINEL_DO_NOT_USE_:
      NOTREACHED();
  }
}

// static
bool EnumTraits<MojomFileType, ProtoImageFormat>::FromMojom(
    MojomFileType input,
    ProtoImageFormat* out) {
  switch (input) {
    case MojomFileType::kPng:
      *out = ProtoImageFormat::IMAGE_FORMAT_PNG;
      return true;
    // PDF images request JPEG data from lorgnette, then
    // convert the returned JPEG data to PDF.
    case MojomFileType::kPdf:  // FALLTHROUGH
    case MojomFileType::kJpg:
      *out = ProtoImageFormat::IMAGE_FORMAT_JPEG;
      return true;
  }
  NOTREACHED();
}

// static
MojomScanResult EnumTraits<MojomScanResult, ProtoScanFailureMode>::ToMojom(
    ProtoScanFailureMode input) {
  switch (input) {
    case ProtoScanFailureMode::SCAN_FAILURE_MODE_NO_FAILURE:
      return MojomScanResult::kSuccess;
    case ProtoScanFailureMode::SCAN_FAILURE_MODE_UNKNOWN:
      return MojomScanResult::kUnknownError;
    case ProtoScanFailureMode::SCAN_FAILURE_MODE_DEVICE_BUSY:
      return MojomScanResult::kDeviceBusy;
    case ProtoScanFailureMode::SCAN_FAILURE_MODE_ADF_JAMMED:
      return MojomScanResult::kAdfJammed;
    case ProtoScanFailureMode::SCAN_FAILURE_MODE_ADF_EMPTY:
      return MojomScanResult::kAdfEmpty;
    case ProtoScanFailureMode::SCAN_FAILURE_MODE_FLATBED_OPEN:
      return MojomScanResult::kFlatbedOpen;
    case ProtoScanFailureMode::SCAN_FAILURE_MODE_IO_ERROR:
      return MojomScanResult::kIoError;
    case ProtoScanFailureMode::ScanFailureMode_INT_MIN_SENTINEL_DO_NOT_USE_:
    case ProtoScanFailureMode::ScanFailureMode_INT_MAX_SENTINEL_DO_NOT_USE_:
      break;
  }
  NOTREACHED();
}

// static
bool EnumTraits<MojomScanResult, ProtoScanFailureMode>::FromMojom(
    MojomScanResult input,
    ProtoScanFailureMode* output) {
  switch (input) {
    case MojomScanResult::kSuccess:
      *output = ProtoScanFailureMode::SCAN_FAILURE_MODE_NO_FAILURE;
      return true;
    case MojomScanResult::kUnknownError:
      *output = ProtoScanFailureMode::SCAN_FAILURE_MODE_UNKNOWN;
      return true;
    case MojomScanResult::kDeviceBusy:
      *output = ProtoScanFailureMode::SCAN_FAILURE_MODE_DEVICE_BUSY;
      return true;
    case MojomScanResult::kAdfJammed:
      *output = ProtoScanFailureMode::SCAN_FAILURE_MODE_ADF_JAMMED;
      return true;
    case MojomScanResult::kAdfEmpty:
      *output = ProtoScanFailureMode::SCAN_FAILURE_MODE_ADF_EMPTY;
      return true;
    case MojomScanResult::kFlatbedOpen:
      *output = ProtoScanFailureMode::SCAN_FAILURE_MODE_FLATBED_OPEN;
      return true;
    case MojomScanResult::kIoError:
      *output = ProtoScanFailureMode::SCAN_FAILURE_MODE_IO_ERROR;
      return true;
  }
  NOTREACHED();
}

// static
mojo_ipc::ScannerCapabilitiesPtr
StructTraits<ash::scanning::mojom::ScannerCapabilitiesPtr,
             lorgnette::ScannerCapabilities>::
    ToMojom(const lorgnette::ScannerCapabilities& lorgnette_caps) {
  mojo_ipc::ScannerCapabilities mojo_caps;
  mojo_caps.sources.reserve(lorgnette_caps.sources().size());
  for (const auto& source : lorgnette_caps.sources()) {
    mojo_ipc::ScanSourcePtr mojo_source = mojo_ipc::ScanSource::New();
    mojo_source->type =
        mojo::EnumTraits<mojo_ipc::SourceType, lorgnette::SourceType>::ToMojom(
            source.type());
    mojo_source->name = source.name();
    mojo_source->page_sizes = GetSupportedPageSizes(source.area());

    mojo_source->color_modes.reserve(source.color_modes().size());
    for (const auto& mode : source.color_modes()) {
      mojo_source->color_modes.push_back(
          mojo::EnumTraits<mojo_ipc::ColorMode, lorgnette::ColorMode>::ToMojom(
              static_cast<lorgnette::ColorMode>(mode)));
    }

    mojo_source->resolutions.reserve(source.resolutions().size());
    for (const auto& res : source.resolutions())
      mojo_source->resolutions.push_back(res);

    mojo_caps.sources.push_back(std::move(mojo_source));
  }

  return mojo_caps.Clone();
}

// static
lorgnette::ScanSettings
StructTraits<lorgnette::ScanSettings, mojo_ipc::ScanSettingsPtr>::ToMojom(
    const mojo_ipc::ScanSettingsPtr& mojo_settings) {
  lorgnette::ScanSettings lorgnette_settings;
  lorgnette::ColorMode lorgnette_color_mode;
  lorgnette::ImageFormat lorgnette_image_format;

  if (mojo::EnumTraits<mojo_ipc::ColorMode, lorgnette::ColorMode>::FromMojom(
          mojo_settings->color_mode, &lorgnette_color_mode)) {
    lorgnette_settings.set_color_mode(lorgnette_color_mode);
  }
  if (mojo::EnumTraits<mojo_ipc::FileType, lorgnette::ImageFormat>::FromMojom(
          mojo_settings->file_type, &lorgnette_image_format)) {
    lorgnette_settings.set_image_format(lorgnette_image_format);
  }

  lorgnette_settings.set_source_name(mojo_settings->source_name);
  lorgnette_settings.set_resolution(mojo_settings->resolution_dpi);
  SetScanRegion(mojo_settings->page_size, lorgnette_settings);
  return lorgnette_settings;
}

}  // namespace mojo