Commit 7cf1750e authored by Kyra Moed's avatar Kyra Moed Committed by Commit Bot

scanning: Support JPG file format in ScanService

Implement conversion from PNG to JPG image format when requested by user
on scanning UI. Add unit tests to verify functionality.

Bug: b:161237869
Change-Id: I8d21a0d7ddff0ddb3635b45b133f3869ec3e61cf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2507574
Commit-Queue: Kyra Moed <kmoed@google.com>
Reviewed-by: default avatarJesse Schettler <jschettler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#823330}
parent baec4bfb
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chrome/browser/chromeos/scanning/scan_service.h" #include "chrome/browser/chromeos/scanning/scan_service.h"
#include <cstdint>
#include <utility> #include <utility>
#include "base/bind.h" #include "base/bind.h"
...@@ -15,6 +16,8 @@ ...@@ -15,6 +16,8 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h" #include "chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h"
#include "chrome/browser/chromeos/scanning/scanning_type_converters.h" #include "chrome/browser/chromeos/scanning/scanning_type_converters.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_util.h"
namespace chromeos { namespace chromeos {
...@@ -25,6 +28,22 @@ namespace mojo_ipc = scanning::mojom; ...@@ -25,6 +28,22 @@ namespace mojo_ipc = scanning::mojom;
// Path to the active user's "My files" folder. // Path to the active user's "My files" folder.
constexpr char kActiveUserMyFilesPath[] = "/home/chronos/user/MyFiles"; constexpr char kActiveUserMyFilesPath[] = "/home/chronos/user/MyFiles";
// The conversion quality when converting from PNG to JPG.
constexpr int kJpgQuality = 100;
// Converts |png_img| to JPG.
std::string PngToJpg(const std::string& png_img) {
std::vector<uint8_t> jpg_img;
const gfx::Image img = gfx::Image::CreateFrom1xPNGBytes(
reinterpret_cast<const unsigned char*>(png_img.c_str()), png_img.size());
if (!gfx::JPEG1xEncodedDataFromImage(img, kJpgQuality, &jpg_img)) {
LOG(ERROR) << "Failed to convert image from PNG to JPG.";
return "";
}
return std::string(jpg_img.begin(), jpg_img.end());
}
} // namespace } // namespace
ScanService::ScanService(LorgnetteScannerManager* lorgnette_scanner_manager, ScanService::ScanService(LorgnetteScannerManager* lorgnette_scanner_manager,
...@@ -132,18 +151,30 @@ void ScanService::OnPageReceived(const base::FilePath& scan_to_path, ...@@ -132,18 +151,30 @@ void ScanService::OnPageReceived(const base::FilePath& scan_to_path,
const mojo_ipc::FileType file_type, const mojo_ipc::FileType file_type,
std::string scanned_image, std::string scanned_image,
uint32_t page_number) { uint32_t page_number) {
// TODO(jschettler): Add support for converting the scanned image to other std::string filename;
// file types. std::string file_ext;
if (file_type != mojo_ipc::FileType::kPng) { switch (file_type) {
LOG(ERROR) << "Selected file type not supported."; case mojo_ipc::FileType::kPng:
save_failed_ = true; file_ext = "png";
return; break;
case mojo_ipc::FileType::kJpg:
file_ext = "jpg";
scanned_image = PngToJpg(scanned_image);
if (scanned_image == "") {
save_failed_ = true;
return;
}
break;
default:
LOG(ERROR) << "Selected file type not supported.";
save_failed_ = true;
return;
} }
const std::string filename = base::StringPrintf( filename = base::StringPrintf(
"scan_%02d%02d%02d-%02d%02d%02d_%d.png", start_time_.year, "scan_%02d%02d%02d-%02d%02d%02d_%d.%s", start_time_.year,
start_time_.month, start_time_.day_of_month, start_time_.hour, start_time_.month, start_time_.day_of_month, start_time_.hour,
start_time_.minute, start_time_.second, page_number); start_time_.minute, start_time_.second, page_number, file_ext.c_str());
const auto file_path = scan_to_path.Append(filename); const auto file_path = scan_to_path.Append(filename);
if (!base::WriteFile(file_path, scanned_image)) { if (!base::WriteFile(file_path, scanned_image)) {
LOG(ERROR) << "Failed to save scanned image: " << file_path.value().c_str(); LOG(ERROR) << "Failed to save scanned image: " << file_path.value().c_str();
......
...@@ -4,8 +4,11 @@ ...@@ -4,8 +4,11 @@
#include "chrome/browser/chromeos/scanning/scan_service.h" #include "chrome/browser/chromeos/scanning/scan_service.h"
#include <map>
#include <string>
#include <vector> #include <vector>
#include "base/containers/flat_set.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
...@@ -222,19 +225,24 @@ TEST_F(ScanServiceTest, Scan) { ...@@ -222,19 +225,24 @@ TEST_F(ScanServiceTest, Scan) {
base::Time::Exploded scan_time; base::Time::Exploded scan_time;
// Since we're using mock time, this is deterministic. // Since we're using mock time, this is deterministic.
base::Time::Now().LocalExplode(&scan_time); base::Time::Now().LocalExplode(&scan_time);
base::FilePath saved_scan_path = temp_dir_.GetPath().Append(
base::StringPrintf("scan_%02d%02d%02d-%02d%02d%02d_1.png", scan_time.year,
scan_time.month, scan_time.day_of_month,
scan_time.hour, scan_time.minute, scan_time.second));
EXPECT_FALSE(base::PathExists(saved_scan_path));
// Saving scanned images is currently only supported for the PNG file type.
scan_service_.SetMyFilesPathForTesting(temp_dir_.GetPath()); scan_service_.SetMyFilesPathForTesting(temp_dir_.GetPath());
mojo_ipc::ScanSettings settings; mojo_ipc::ScanSettings settings;
settings.scan_to_path = temp_dir_.GetPath(); settings.scan_to_path = temp_dir_.GetPath();
settings.file_type = mojo_ipc::FileType::kPng; std::map<std::string, mojo_ipc::FileType> file_types = {
EXPECT_TRUE(Scan(scanners[0]->id, settings.Clone())); {"png", mojo_ipc::FileType::kPng}, {"jpg", mojo_ipc::FileType::kJpg}};
EXPECT_TRUE(base::PathExists(saved_scan_path)); base::FilePath saved_scan_path;
for (const auto& type : file_types) {
saved_scan_path = temp_dir_.GetPath().Append(base::StringPrintf(
"scan_%02d%02d%02d-%02d%02d%02d_1.%s", scan_time.year, scan_time.month,
scan_time.day_of_month, scan_time.hour, scan_time.minute,
scan_time.second, type.first.c_str()));
EXPECT_FALSE(base::PathExists(saved_scan_path));
settings.file_type = type.second;
EXPECT_TRUE(Scan(scanners[0]->id, settings.Clone()));
EXPECT_TRUE(base::PathExists(saved_scan_path));
}
} }
} // namespace chromeos } // namespace chromeos
...@@ -26,7 +26,7 @@ import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bun ...@@ -26,7 +26,7 @@ import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bun
import {getScanService} from './mojo_interface_provider.js'; import {getScanService} from './mojo_interface_provider.js';
import {ScannerArr} from './scanning_app_types.js'; import {ScannerArr} from './scanning_app_types.js';
import {colorModeFromString, pageSizeFromString, tokenToString} from './scanning_app_util.js'; import {colorModeFromString, fileTypeFromString, pageSizeFromString, tokenToString} from './scanning_app_util.js';
/** /**
* The default save directory for completed scans. * The default save directory for completed scans.
...@@ -227,10 +227,10 @@ Polymer({ ...@@ -227,10 +227,10 @@ Polymer({
return; return;
} }
// TODO(jschettler): Remove this once ScanService supports other file types. // TODO(jschettler): Remove this once ScanService supports PDF.
if (this.selectedFileType !== if (this.selectedFileType ==
chromeos.scanning.mojom.FileType.kPng.toString()) { chromeos.scanning.mojom.FileType.kPdf.toString()) {
this.statusText_ = 'PNG is the only supported file type.'; this.statusText_ = 'PDF is not a supported file type.';
return; return;
} }
...@@ -238,12 +238,10 @@ Polymer({ ...@@ -238,12 +238,10 @@ Polymer({
this.settingsDisabled_ = true; this.settingsDisabled_ = true;
this.scanButtonDisabled_ = true; this.scanButtonDisabled_ = true;
// TODO(jschettler): Use the selected file type when ScanService supports
// it.
const settings = { const settings = {
'sourceName': this.selectedSource, 'sourceName': this.selectedSource,
'scanToPath': {'path': this.selectedFilePath}, 'scanToPath': {'path': this.selectedFilePath},
'fileType': chromeos.scanning.mojom.FileType.kPng, 'fileType': fileTypeFromString(this.selectedFileType),
'colorMode': colorModeFromString(this.selectedColorMode), 'colorMode': colorModeFromString(this.selectedColorMode),
'pageSize': pageSizeFromString(this.selectedPageSize), 'pageSize': pageSizeFromString(this.selectedPageSize),
'resolutionDpi': Number(this.selectedResolution), 'resolutionDpi': Number(this.selectedResolution),
......
...@@ -24,6 +24,26 @@ export function colorModeFromString(colorModeString) { ...@@ -24,6 +24,26 @@ export function colorModeFromString(colorModeString) {
} }
} }
/**
* Converts a chromeos.scanning.mojom.FileType string to the corresponding
* enum value.
* @param {string} fileTypeString
* @return {chromeos.scanning.mojom.FileType}
*/
export function fileTypeFromString(fileTypeString) {
switch (fileTypeString) {
case chromeos.scanning.mojom.FileType.kJpg.toString():
return chromeos.scanning.mojom.FileType.kJpg;
case chromeos.scanning.mojom.FileType.kPdf.toString():
return chromeos.scanning.mojom.FileType.kPdf;
case chromeos.scanning.mojom.FileType.kPng.toString():
return chromeos.scanning.mojom.FileType.kPng;
default:
assertNotReached();
return chromeos.scanning.mojom.FileType.kPdf;
}
}
/** /**
* Converts a chromeos.scanning.mojom.ColorMode to a string that can be * Converts a chromeos.scanning.mojom.ColorMode to a string that can be
* displayed in the color mode dropdown. * displayed in the color mode dropdown.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment