Preprocess images before passport upload.

This commit is contained in:
John Preston 2018-04-20 21:34:51 +04:00
parent ab5f35e952
commit ab797b4dff
6 changed files with 114 additions and 6 deletions

View File

@ -1612,6 +1612,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passport_stop" = "Stop"; "lng_passport_stop" = "Stop";
"lng_passport_restart_sure" = "Unexpected error has occurred. Perhaps some changes were done from a different Telegram application. Would you like to restart this authorization?"; "lng_passport_restart_sure" = "Unexpected error has occurred. Perhaps some changes were done from a different Telegram application. Would you like to restart this authorization?";
"lng_passport_restart" = "Restart"; "lng_passport_restart" = "Restart";
"lng_passport_error_too_large" = "This file is too large.";
"lng_passport_error_bad_size" = "This image has bad dimensions.";
"lng_passport_error_cant_read" = "Can't read this file. Please choose an image.";
// Wnd specific // Wnd specific

View File

@ -54,5 +54,9 @@ inline QString PassportCorruptedResetSure() {
return qsl("Are you sure you want to reset your Telegram Passport data?"); return qsl("Are you sure you want to reset your Telegram Passport data?");
} }
inline QString UnknownSecureScanError() {
return qsl("Unknown scan read error.");
}
} // namespace Hard } // namespace Hard
} // namespace Lang } // namespace Lang

View File

@ -843,9 +843,27 @@ void PanelController::editWithUpload(int index, int documentIndex) {
base::take(_scopeDocumentTypeBox); base::take(_scopeDocumentTypeBox);
editScope(index, documentIndex); editScope(index, documentIndex);
uploadScan(std::move(content)); uploadScan(std::move(content));
}, [=](ReadScanError error) {
readScanError(error);
}); });
} }
void PanelController::readScanError(ReadScanError error) {
show(Box<InformBox>([&] {
switch (error) {
case ReadScanError::FileTooLarge:
return lang(lng_passport_error_too_large);
case ReadScanError::BadImageSize:
return lang(lng_passport_error_bad_size);
case ReadScanError::CantReadImage:
return lang(lng_passport_error_cant_read);
case ReadScanError::Unknown:
return Lang::Hard::UnknownSecureScanError();
}
Unexpected("Error type in PanelController::readScanError.");
}()));
}
void PanelController::editScope(int index, int documentIndex) { void PanelController::editScope(int index, int documentIndex) {
Expects(_panel != nullptr); Expects(_panel != nullptr);
Expects(index >= 0 && index < _scopes.size()); Expects(index >= 0 && index < _scopes.size());

View File

@ -18,6 +18,8 @@ class Panel;
struct EditDocumentScheme; struct EditDocumentScheme;
struct EditContactScheme; struct EditContactScheme;
enum class ReadScanError;
EditDocumentScheme GetDocumentScheme( EditDocumentScheme GetDocumentScheme(
Scope::Type type, Scope::Type type,
base::optional<Value::Type> scansType = base::none); base::optional<Value::Type> scansType = base::none);
@ -84,6 +86,7 @@ public:
void restoreSelfie(); void restoreSelfie();
rpl::producer<ScanInfo> scanUpdated() const; rpl::producer<ScanInfo> scanUpdated() const;
rpl::producer<ScopeError> saveErrors() const; rpl::producer<ScopeError> saveErrors() const;
void readScanError(ReadScanError error);
base::optional<rpl::producer<QString>> deleteValueLabel() const; base::optional<rpl::producer<QString>> deleteValueLabel() const;
void deleteValue(); void deleteValue();

View File

@ -20,9 +20,49 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/file_utilities.h" #include "core/file_utilities.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "boxes/abstract_box.h" #include "boxes/abstract_box.h"
#include "storage/storage_media_prepare.h"
#include "styles/style_passport.h" #include "styles/style_passport.h"
namespace Passport { namespace Passport {
namespace {
constexpr auto kMaxDimensions = 2048;
constexpr auto kMaxSize = 10 * 1024 * 1024;
constexpr auto kJpegQuality = 89;
static_assert(kMaxSize <= UseBigFilesFrom);
base::variant<ReadScanError, QByteArray> ProcessImage(QByteArray &&bytes) {
auto image = App::readImage(base::take(bytes));
if (image.isNull()) {
return ReadScanError::CantReadImage;
} else if (!Storage::ValidateThumbDimensions(image.width(), image.height())) {
return ReadScanError::BadImageSize;
}
if (std::max(image.width(), image.height()) > kMaxDimensions) {
image = std::move(image).scaled(
kMaxDimensions,
kMaxDimensions,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
}
auto result = QByteArray();
{
QBuffer buffer(&result);
if (!image.save(&buffer, QByteArray("JPG"), kJpegQuality)) {
return ReadScanError::Unknown;
}
base::take(image);
}
if (result.isEmpty()) {
return ReadScanError::Unknown;
} else if (result.size() > kMaxSize) {
return ReadScanError::FileTooLarge;
}
return result;
}
} // namespace
class ScanButton : public Ui::AbstractButton { class ScanButton : public Ui::AbstractButton {
public: public:
@ -496,39 +536,71 @@ void EditScans::chooseScan() {
} }
ChooseScan(this, [=](QByteArray &&content) { ChooseScan(this, [=](QByteArray &&content) {
_controller->uploadScan(std::move(content)); _controller->uploadScan(std::move(content));
}, [=](ReadScanError error) {
_controller->readScanError(error);
}); });
} }
void EditScans::chooseSelfie() { void EditScans::chooseSelfie() {
ChooseScan(this, [=](QByteArray &&content) { ChooseScan(this, [=](QByteArray &&content) {
_controller->uploadSelfie(std::move(content)); _controller->uploadSelfie(std::move(content));
}, [=](ReadScanError error) {
_controller->readScanError(error);
}); });
} }
void EditScans::ChooseScan( void EditScans::ChooseScan(
QPointer<QWidget> parent, QPointer<QWidget> parent,
base::lambda<void(QByteArray&&)> callback) { base::lambda<void(QByteArray&&)> doneCallback,
base::lambda<void(ReadScanError)> errorCallback) {
Expects(parent != nullptr); Expects(parent != nullptr);
const auto filter = FileDialog::AllFilesFilter() const auto filter = FileDialog::AllFilesFilter()
+ qsl(";;Image files (*") + qsl(";;Image files (*")
+ cImgExtensions().join(qsl(" *")) + cImgExtensions().join(qsl(" *"))
+ qsl(")"); + qsl(")");
const auto guardedCallback = base::lambda_guarded(parent, callback); const auto guardedCallback = base::lambda_guarded(parent, doneCallback);
const auto guardedError = base::lambda_guarded(parent, errorCallback);
const auto onMainCallback = [=](QByteArray content) {
crl::on_main([=, bytes = std::move(content)]() mutable {
guardedCallback(std::move(bytes));
});
};
const auto onMainError = [=](ReadScanError error) {
crl::on_main([=] {
guardedError(error);
});
};
const auto processImage = [=](QByteArray &&content) {
crl::async([=, bytes = std::move(content)]() mutable {
auto result = ProcessImage(std::move(bytes));
if (const auto error = base::get_if<ReadScanError>(&result)) {
onMainError(*error);
} else {
auto content = base::get_if<QByteArray>(&result);
Assert(content != nullptr);
onMainCallback(std::move(*content));
}
});
};
const auto processFile = [=](FileDialog::OpenResult &&result) { const auto processFile = [=](FileDialog::OpenResult &&result) {
if (result.paths.size() == 1) { if (result.paths.size() == 1) {
auto content = [&] { auto content = [&] {
QFile f(result.paths.front()); QFile f(result.paths.front());
if (!f.open(QIODevice::ReadOnly)) { if (f.size() > App::kImageSizeLimit) {
guardedError(ReadScanError::FileTooLarge);
return QByteArray();
} else if (!f.open(QIODevice::ReadOnly)) {
guardedError(ReadScanError::CantReadImage);
return QByteArray(); return QByteArray();
} }
return f.readAll(); return f.readAll();
}(); }();
if (!content.isEmpty()) { if (!content.isEmpty()) {
guardedCallback(std::move(content)); processImage(std::move(content));
} }
} else if (!result.remoteContent.isEmpty()) { } else if (!result.remoteContent.isEmpty()) {
guardedCallback(std::move(result.remoteContent)); processImage(std::move(result.remoteContent));
} }
}; };
FileDialog::GetOpenPath( FileDialog::GetOpenPath(

View File

@ -30,6 +30,13 @@ class PanelController;
class ScanButton; class ScanButton;
struct ScanInfo; struct ScanInfo;
enum class ReadScanError {
FileTooLarge,
CantReadImage,
BadImageSize,
Unknown,
};
class EditScans : public Ui::RpWidget { class EditScans : public Ui::RpWidget {
public: public:
EditScans( EditScans(
@ -44,7 +51,8 @@ public:
static void ChooseScan( static void ChooseScan(
QPointer<QWidget> parent, QPointer<QWidget> parent,
base::lambda<void(QByteArray&&)> callback); base::lambda<void(QByteArray&&)> doneCallback,
base::lambda<void(ReadScanError)> errorCallback);
private: private:
void setupContent(const QString &header); void setupContent(const QString &header);