@autonomdev/ngx-document-scanner
Version:
Angular 2+ component for cropping and enhancing images of documents
1,355 lines • 115 kB
JavaScript
/**
* @fileoverview added by tsickle
* Generated from: lib/components/image-editor/ngx-doc-scanner.component.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import { __awaiter, __generator, __read, __spread } from "tslib";
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { LimitsService, PositionChangeData } from '../../services/limits.service';
import { MatBottomSheet } from '@angular/material/bottom-sheet';
import { NgxFilterMenuComponent } from '../filter-menu/ngx-filter-menu.component';
import { NgxOpenCVService } from 'ngx-opencv';
var NgxDocScannerComponent = /** @class */ (function () {
function NgxDocScannerComponent(ngxOpenCv, limitsService, bottomSheet) {
var _this = this;
this.ngxOpenCv = ngxOpenCv;
this.limitsService = limitsService;
this.bottomSheet = bottomSheet;
// ************* //
// EDITOR CONFIG //
// ************* //
/**
* an array of action buttons displayed on the editor screen
*/
this.editorButtons = [
{
name: 'exit',
action: (/**
* @return {?}
*/
function () {
_this.exitEditor.emit('canceled');
}),
icon: 'arrow_back',
type: 'fab',
mode: 'crop'
},
{
name: 'rotate',
action: this.rotateImage.bind(this),
icon: 'rotate_right',
type: 'fab',
mode: 'crop'
},
{
name: 'done_crop',
action: (/**
* @return {?}
*/
function () { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
this.mode = 'color';
return [4 /*yield*/, this.transform()];
case 1:
_a.sent();
return [4 /*yield*/, this.applyFilter(true)];
case 2:
_a.sent();
return [2 /*return*/];
}
});
}); }),
icon: 'done',
type: 'fab',
mode: 'crop'
},
{
name: 'back',
action: (/**
* @return {?}
*/
function () {
_this.mode = 'crop';
_this.loadFile(_this.originalImage);
}),
icon: 'arrow_back',
type: 'fab',
mode: 'color'
},
{
name: 'filter',
action: (/**
* @return {?}
*/
function () {
return _this.chooseFilters();
}),
icon: 'photo_filter',
type: 'fab',
mode: 'color'
},
{
name: 'upload',
action: this.exportImage.bind(this),
icon: 'cloud_upload',
type: 'fab',
mode: 'color'
},
];
/**
* true after the image is loaded and preview is displayed
*/
this.imageLoaded = false;
/**
* editor mode
*/
this.mode = 'crop';
/**
* filter selected by the user, returned by the filter selector bottom sheet
*/
this.selectedFilter = 'default';
/**
* image dimensions
*/
this.imageDimensions = {
width: 0,
height: 0
};
// ************** //
// EVENT EMITTERS //
// ************** //
/**
* optional binding to the exit button of the editor
*/
this.exitEditor = new EventEmitter();
/**
* fires on edit completion
*/
this.editResult = new EventEmitter();
/**
* emits errors, can be linked to an error handler of choice
*/
this.error = new EventEmitter();
/**
* emits the loading status of the cv module.
*/
this.ready = new EventEmitter();
/**
* emits true when processing is done, false when completed
*/
this.processing = new EventEmitter();
this.screenDimensions = {
width: window.innerWidth,
height: window.innerHeight
};
// subscribe to status of cv module
this.ngxOpenCv.cvState.subscribe((/**
* @param {?} cvState
* @return {?}
*/
function (cvState) {
_this.cvState = cvState.state;
_this.ready.emit(cvState.ready);
if (cvState.error) {
_this.error.emit(new Error('error loading cv'));
}
else if (cvState.loading) {
_this.processing.emit(true);
}
else if (cvState.ready) {
_this.processing.emit(false);
}
}));
// subscribe to positions of crop tool
this.limitsService.positions.subscribe((/**
* @param {?} points
* @return {?}
*/
function (points) {
_this.points = points;
}));
}
Object.defineProperty(NgxDocScannerComponent.prototype, "displayedButtons", {
/**
* returns an array of buttons according to the editor mode
*/
get: /**
* returns an array of buttons according to the editor mode
* @return {?}
*/
function () {
var _this = this;
return this.editorButtons.filter((/**
* @param {?} button
* @return {?}
*/
function (button) {
return button.mode === _this.mode;
}));
},
enumerable: true,
configurable: true
});
Object.defineProperty(NgxDocScannerComponent.prototype, "file", {
// ****** //
// INPUTS //
// ****** //
/**
* set image for editing
* @param file - file from form input
*/
set:
// ****** //
// INPUTS //
// ****** //
/**
* set image for editing
* @param {?} file - file from form input
* @return {?}
*/
function (file) {
var _this = this;
if (file) {
setTimeout((/**
* @return {?}
*/
function () {
_this.processing.emit(true);
}), 5);
this.imageLoaded = false;
this.originalImage = file;
this.ngxOpenCv.cvState.subscribe((/**
* @param {?} cvState
* @return {?}
*/
function (cvState) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!cvState.ready) return [3 /*break*/, 2];
// read file to image & canvas
return [4 /*yield*/, this.loadFile(file)];
case 1:
// read file to image & canvas
_a.sent();
this.processing.emit(false);
_a.label = 2;
case 2: return [2 /*return*/];
}
});
}); }));
}
},
enumerable: true,
configurable: true
});
/**
* @return {?}
*/
NgxDocScannerComponent.prototype.ngOnInit = /**
* @return {?}
*/
function () {
var _this = this;
// set options from config object
this.options = new ImageEditorConfig(this.config);
// set export image icon
this.editorButtons.forEach((/**
* @param {?} button
* @return {?}
*/
function (button) {
if (button.name === 'upload') {
button.icon = _this.options.exportImageIcon;
}
}));
this.maxPreviewWidth = this.options.maxPreviewWidth;
this.editorStyle = this.options.editorStyle;
};
// ***************************** //
// editor action buttons methods //
// ***************************** //
/**
* emits the exitEditor event
*/
// ***************************** //
// editor action buttons methods //
// ***************************** //
/**
* emits the exitEditor event
* @return {?}
*/
NgxDocScannerComponent.prototype.exit =
// ***************************** //
// editor action buttons methods //
// ***************************** //
/**
* emits the exitEditor event
* @return {?}
*/
function () {
this.exitEditor.emit('canceled');
};
/**
* applies the selected filter, and when done emits the resulted image
*/
/**
* applies the selected filter, and when done emits the resulted image
* @private
* @return {?}
*/
NgxDocScannerComponent.prototype.exportImage = /**
* applies the selected filter, and when done emits the resulted image
* @private
* @return {?}
*/
function () {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.applyFilter(false)];
case 1:
_a.sent();
if (this.options.maxImageDimensions) {
this.resize(this.editedImage)
.then((/**
* @param {?} resizeResult
* @return {?}
*/
function (resizeResult) {
resizeResult.toBlob((/**
* @param {?} blob
* @return {?}
*/
function (blob) {
_this.editResult.emit(blob);
_this.processing.emit(false);
}), _this.originalImage.type);
}));
}
else {
this.editedImage.toBlob((/**
* @param {?} blob
* @return {?}
*/
function (blob) {
_this.editResult.emit(blob);
_this.processing.emit(false);
}), this.originalImage.type);
}
return [2 /*return*/];
}
});
});
};
/**
* open the bottom sheet for selecting filters, and applies the selected filter in preview mode
*/
/**
* open the bottom sheet for selecting filters, and applies the selected filter in preview mode
* @private
* @return {?}
*/
NgxDocScannerComponent.prototype.chooseFilters = /**
* open the bottom sheet for selecting filters, and applies the selected filter in preview mode
* @private
* @return {?}
*/
function () {
var _this = this;
/** @type {?} */
var data = { filter: this.selectedFilter };
/** @type {?} */
var bottomSheetRef = this.bottomSheet.open(NgxFilterMenuComponent, {
data: data
});
bottomSheetRef.afterDismissed().subscribe((/**
* @return {?}
*/
function () {
_this.selectedFilter = data.filter;
_this.applyFilter(true);
}));
};
// *************************** //
// File Input & Output Methods //
// *************************** //
/**
* load image from input field
*/
// *************************** //
// File Input & Output Methods //
// *************************** //
/**
* load image from input field
* @private
* @param {?} file
* @return {?}
*/
NgxDocScannerComponent.prototype.loadFile =
// *************************** //
// File Input & Output Methods //
// *************************** //
/**
* load image from input field
* @private
* @param {?} file
* @return {?}
*/
function (file) {
var _this = this;
return new Promise((/**
* @param {?} resolve
* @param {?} reject
* @return {?}
*/
function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
var err_1, err_2;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
this.processing.emit(true);
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.readImage(file)];
case 2:
_a.sent();
return [3 /*break*/, 4];
case 3:
err_1 = _a.sent();
console.error(err_1);
this.error.emit(new Error(err_1));
return [3 /*break*/, 4];
case 4:
_a.trys.push([4, 6, , 7]);
return [4 /*yield*/, this.showPreview()];
case 5:
_a.sent();
return [3 /*break*/, 7];
case 6:
err_2 = _a.sent();
console.error(err_2);
this.error.emit(new Error(err_2));
return [3 /*break*/, 7];
case 7:
// set pane limits
// show points
this.imageLoaded = true;
return [4 /*yield*/, this.limitsService.setPaneDimensions({ width: this.previewDimensions.width, height: this.previewDimensions.height })];
case 8:
_a.sent();
setTimeout((/**
* @return {?}
*/
function () { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.detectContours()];
case 1:
_a.sent();
this.processing.emit(false);
resolve();
return [2 /*return*/];
}
});
}); }), 15);
return [2 /*return*/];
}
});
}); }));
};
/**
* read image from File object
*/
/**
* read image from File object
* @private
* @param {?} file
* @return {?}
*/
NgxDocScannerComponent.prototype.readImage = /**
* read image from File object
* @private
* @param {?} file
* @return {?}
*/
function (file) {
var _this = this;
return new Promise((/**
* @param {?} resolve
* @param {?} reject
* @return {?}
*/
function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
var imageSrc, err_3, img;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, readFile()];
case 1:
imageSrc = _a.sent();
return [3 /*break*/, 3];
case 2:
err_3 = _a.sent();
reject(err_3);
return [3 /*break*/, 3];
case 3:
img = new Image();
img.onload = (/**
* @return {?}
*/
function () { return __awaiter(_this, void 0, void 0, function () {
var ctx, width, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
// set edited image canvas and dimensions
this.editedImage = (/** @type {?} */ (document.createElement('canvas')));
this.editedImage.width = img.width;
this.editedImage.height = img.height;
ctx = this.editedImage.getContext('2d');
ctx.drawImage(img, 0, 0);
// resize image if larger than max image size
width = img.width > img.height ? img.height : img.width;
if (!(width > this.options.maxImageDimensions.width)) return [3 /*break*/, 2];
_a = this;
return [4 /*yield*/, this.resize(this.editedImage)];
case 1:
_a.editedImage = _b.sent();
_b.label = 2;
case 2:
this.imageDimensions.width = this.editedImage.width;
this.imageDimensions.height = this.editedImage.height;
this.setPreviewPaneDimensions(this.editedImage);
resolve();
return [2 /*return*/];
}
});
}); });
img.src = imageSrc;
return [2 /*return*/];
}
});
}); }));
/**
* read file from input field
* @return {?}
*/
function readFile() {
return new Promise((/**
* @param {?} resolve
* @param {?} reject
* @return {?}
*/
function (resolve, reject) {
/** @type {?} */
var reader = new FileReader();
reader.onload = (/**
* @param {?} event
* @return {?}
*/
function (event) {
resolve(reader.result);
});
reader.onerror = (/**
* @param {?} err
* @return {?}
*/
function (err) {
reject(err);
});
reader.readAsDataURL(file);
}));
}
};
// ************************ //
// Image Processing Methods //
// ************************ //
/**
* rotate image 90 degrees
*/
// ************************ //
// Image Processing Methods //
// ************************ //
/**
* rotate image 90 degrees
* @private
* @return {?}
*/
NgxDocScannerComponent.prototype.rotateImage =
// ************************ //
// Image Processing Methods //
// ************************ //
/**
* rotate image 90 degrees
* @private
* @return {?}
*/
function () {
var _this = this;
return new Promise((/**
* @param {?} resolve
* @param {?} reject
* @return {?}
*/
function (resolve, reject) {
_this.processing.emit(true);
setTimeout((/**
* @return {?}
*/
function () {
/** @type {?} */
var dst = cv.imread(_this.editedImage);
// const dst = new cv.Mat();
cv.transpose(dst, dst);
cv.flip(dst, dst, 1);
cv.imshow(_this.editedImage, dst);
// src.delete();
dst.delete();
// save current preview dimensions and positions
/** @type {?} */
var initialPreviewDimensions = { width: 0, height: 0 };
Object.assign(initialPreviewDimensions, _this.previewDimensions);
/** @type {?} */
var initialPositions = Array.from(_this.points);
// get new dimensions
// set new preview pane dimensions
_this.setPreviewPaneDimensions(_this.editedImage);
// get preview pane resize ratio
/** @type {?} */
var previewResizeRatios = {
width: _this.previewDimensions.width / initialPreviewDimensions.width,
height: _this.previewDimensions.height / initialPreviewDimensions.height
};
// set new preview pane dimensions
_this.limitsService.rotateClockwise(previewResizeRatios, initialPreviewDimensions, initialPositions);
_this.showPreview().then((/**
* @return {?}
*/
function () {
_this.processing.emit(false);
resolve();
}));
}), 30);
}));
};
/**
* detects the contours of the document and
**/
/**
* detects the contours of the document and
*
* @private
* @return {?}
*/
NgxDocScannerComponent.prototype.detectContours = /**
* detects the contours of the document and
*
* @private
* @return {?}
*/
function () {
var _this = this;
return new Promise((/**
* @param {?} resolve
* @param {?} reject
* @return {?}
*/
function (resolve, reject) {
_this.processing.emit(true);
setTimeout((/**
* @return {?}
*/
function () {
// load the image and compute the ratio of the old height to the new height, clone it, and resize it
/** @type {?} */
var processingResizeRatio = 0.5;
/** @type {?} */
var dst = cv.imread(_this.editedImage);
/** @type {?} */
var dsize = new cv.Size(dst.rows * processingResizeRatio, dst.cols * processingResizeRatio);
/** @type {?} */
var ksize = new cv.Size(5, 5);
// convert the image to grayscale, blur it, and find edges in the image
cv.cvtColor(dst, dst, cv.COLOR_RGBA2GRAY, 0);
cv.GaussianBlur(dst, dst, ksize, 0, 0, cv.BORDER_DEFAULT);
cv.Canny(dst, dst, 75, 200);
// find contours
cv.threshold(dst, dst, 120, 200, cv.THRESH_BINARY);
/** @type {?} */
var contours = new cv.MatVector();
/** @type {?} */
var hierarchy = new cv.Mat();
cv.findContours(dst, contours, hierarchy, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE);
/** @type {?} */
var rect = cv.boundingRect(dst);
dst.delete();
hierarchy.delete();
contours.delete();
// transform the rectangle into a set of points
Object.keys(rect).forEach((/**
* @param {?} key
* @return {?}
*/
function (key) {
rect[key] = rect[key] * _this.imageResizeRatio;
}));
/** @type {?} */
var contourCoordinates = [
new PositionChangeData({ x: rect.x, y: rect.y }, ['left', 'top']),
new PositionChangeData({ x: rect.x + rect.width, y: rect.y }, ['right', 'top']),
new PositionChangeData({ x: rect.x + rect.width, y: rect.y + rect.height }, ['right', 'bottom']),
new PositionChangeData({ x: rect.x, y: rect.y + rect.height }, ['left', 'bottom']),
];
_this.limitsService.repositionPoints(contourCoordinates);
// this.processing.emit(false);
resolve();
}), 30);
}));
};
/**
* apply perspective transform
*/
/**
* apply perspective transform
* @private
* @return {?}
*/
NgxDocScannerComponent.prototype.transform = /**
* apply perspective transform
* @private
* @return {?}
*/
function () {
var _this = this;
return new Promise((/**
* @param {?} resolve
* @param {?} reject
* @return {?}
*/
function (resolve, reject) {
_this.processing.emit(true);
setTimeout((/**
* @return {?}
*/
function () {
/** @type {?} */
var dst = cv.imread(_this.editedImage);
// create source coordinates matrix
/** @type {?} */
var sourceCoordinates = [
_this.getPoint(['top', 'left']),
_this.getPoint(['top', 'right']),
_this.getPoint(['bottom', 'right']),
_this.getPoint(['bottom', 'left'])
].map((/**
* @param {?} point
* @return {?}
*/
function (point) {
return [point.x / _this.imageResizeRatio, point.y / _this.imageResizeRatio];
}));
// get max width
/** @type {?} */
var bottomWidth = _this.getPoint(['bottom', 'right']).x - _this.getPoint(['bottom', 'left']).x;
/** @type {?} */
var topWidth = _this.getPoint(['top', 'right']).x - _this.getPoint(['top', 'left']).x;
/** @type {?} */
var maxWidth = Math.max(bottomWidth, topWidth) / _this.imageResizeRatio;
// get max height
/** @type {?} */
var leftHeight = _this.getPoint(['bottom', 'left']).y - _this.getPoint(['top', 'left']).y;
/** @type {?} */
var rightHeight = _this.getPoint(['bottom', 'right']).y - _this.getPoint(['top', 'right']).y;
/** @type {?} */
var maxHeight = Math.max(leftHeight, rightHeight) / _this.imageResizeRatio;
// create dest coordinates matrix
/** @type {?} */
var destCoordinates = [
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]
];
// convert to open cv matrix objects
/** @type {?} */
var Ms = cv.matFromArray(4, 1, cv.CV_32FC2, [].concat.apply([], __spread(sourceCoordinates)));
/** @type {?} */
var Md = cv.matFromArray(4, 1, cv.CV_32FC2, [].concat.apply([], __spread(destCoordinates)));
/** @type {?} */
var transformMatrix = cv.getPerspectiveTransform(Ms, Md);
// set new image size
/** @type {?} */
var dsize = new cv.Size(maxWidth, maxHeight);
// perform warp
cv.warpPerspective(dst, dst, transformMatrix, dsize, cv.INTER_LINEAR, cv.BORDER_CONSTANT, new cv.Scalar());
cv.imshow(_this.editedImage, dst);
dst.delete();
Ms.delete();
Md.delete();
transformMatrix.delete();
_this.setPreviewPaneDimensions(_this.editedImage);
_this.showPreview().then((/**
* @return {?}
*/
function () {
_this.processing.emit(false);
resolve();
}));
}), 30);
}));
};
/**
* applies the selected filter to the image
* @param preview - when true, will not apply the filter to the edited image but only display a preview.
* when false, will apply to editedImage
*/
/**
* applies the selected filter to the image
* @private
* @param {?} preview - when true, will not apply the filter to the edited image but only display a preview.
* when false, will apply to editedImage
* @return {?}
*/
NgxDocScannerComponent.prototype.applyFilter = /**
* applies the selected filter to the image
* @private
* @param {?} preview - when true, will not apply the filter to the edited image but only display a preview.
* when false, will apply to editedImage
* @return {?}
*/
function (preview) {
var _this = this;
return new Promise((/**
* @param {?} resolve
* @param {?} reject
* @return {?}
*/
function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
var options, dst;
var _this = this;
return __generator(this, function (_a) {
this.processing.emit(true);
// default options
options = {
blur: false,
th: true,
thMode: cv.ADAPTIVE_THRESH_MEAN_C,
thMeanCorrection: 10,
thBlockSize: 25,
thMax: 255,
grayScale: true,
};
dst = cv.imread(this.editedImage);
switch (this.selectedFilter) {
case 'default':
options.th = false;
options.grayScale = false;
options.blur = false;
break;
case 'magic_color':
options.grayScale = false;
break;
case 'bw2':
options.thMode = cv.ADAPTIVE_THRESH_GAUSSIAN_C;
options.thMeanCorrection = 15;
options.thBlockSize = 15;
break;
case 'bw3':
options.blur = true;
options.thMeanCorrection = 15;
break;
}
setTimeout((/**
* @return {?}
*/
function () { return __awaiter(_this, void 0, void 0, function () {
var ksize;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (options.grayScale) {
cv.cvtColor(dst, dst, cv.COLOR_RGBA2GRAY, 0);
}
if (options.blur) {
ksize = new cv.Size(5, 5);
cv.GaussianBlur(dst, dst, ksize, 0, 0, cv.BORDER_DEFAULT);
}
if (options.th) {
if (options.grayScale) {
cv.adaptiveThreshold(dst, dst, options.thMax, options.thMode, cv.THRESH_BINARY, options.thBlockSize, options.thMeanCorrection);
}
else {
dst.convertTo(dst, -1, 1, 60);
cv.threshold(dst, dst, 170, 255, cv.THRESH_BINARY);
}
}
if (!preview) {
cv.imshow(this.editedImage, dst);
}
return [4 /*yield*/, this.showPreview(dst)];
case 1:
_a.sent();
this.processing.emit(false);
resolve();
return [2 /*return*/];
}
});
}); }), 30);
return [2 /*return*/];
});
}); }));
};
/**
* resize an image to fit constraints set in options.maxImageDimensions
*/
/**
* resize an image to fit constraints set in options.maxImageDimensions
* @private
* @param {?} image
* @return {?}
*/
NgxDocScannerComponent.prototype.resize = /**
* resize an image to fit constraints set in options.maxImageDimensions
* @private
* @param {?} image
* @return {?}
*/
function (image) {
var _this = this;
return new Promise((/**
* @param {?} resolve
* @param {?} reject
* @return {?}
*/
function (resolve, reject) {
_this.processing.emit(true);
setTimeout((/**
* @return {?}
*/
function () {
/** @type {?} */
var src = cv.imread(image);
/** @type {?} */
var currentDimensions = {
width: src.size().width,
height: src.size().height
};
/** @type {?} */
var resizeDimensions = {
width: 0,
height: 0
};
if (currentDimensions.width > _this.options.maxImageDimensions.width) {
resizeDimensions.width = _this.options.maxImageDimensions.width;
resizeDimensions.height = _this.options.maxImageDimensions.width / currentDimensions.width * currentDimensions.height;
if (resizeDimensions.height > _this.options.maxImageDimensions.height) {
resizeDimensions.height = _this.options.maxImageDimensions.height;
resizeDimensions.width = _this.options.maxImageDimensions.height / currentDimensions.height * currentDimensions.width;
}
/** @type {?} */
var dsize = new cv.Size(Math.floor(resizeDimensions.width), Math.floor(resizeDimensions.height));
cv.resize(src, src, dsize, 0, 0, cv.INTER_AREA);
/** @type {?} */
var resizeResult = (/** @type {?} */ (document.createElement('canvas')));
cv.imshow(resizeResult, src);
src.delete();
_this.processing.emit(false);
resolve(resizeResult);
}
else {
_this.processing.emit(false);
resolve(image);
}
}), 30);
}));
};
/**
* display a preview of the image on the preview canvas
*/
/**
* display a preview of the image on the preview canvas
* @private
* @param {?=} image
* @return {?}
*/
NgxDocScannerComponent.prototype.showPreview = /**
* display a preview of the image on the preview canvas
* @private
* @param {?=} image
* @return {?}
*/
function (image) {
var _this = this;
return new Promise((/**
* @param {?} resolve
* @param {?} reject
* @return {?}
*/
function (resolve, reject) {
/** @type {?} */
var src;
if (image) {
src = image;
}
else {
src = cv.imread(_this.editedImage);
}
/** @type {?} */
var dst = new cv.Mat();
/** @type {?} */
var dsize = new cv.Size(0, 0);
cv.resize(src, dst, dsize, _this.imageResizeRatio, _this.imageResizeRatio, cv.INTER_AREA);
cv.imshow(_this.previewCanvas.nativeElement, dst);
src.delete();
dst.delete();
resolve();
}));
};
// *************** //
// Utility Methods //
// *************** //
/**
* set preview canvas dimensions according to the canvas element of the original image
*/
// *************** //
// Utility Methods //
// *************** //
/**
* set preview canvas dimensions according to the canvas element of the original image
* @private
* @param {?} img
* @return {?}
*/
NgxDocScannerComponent.prototype.setPreviewPaneDimensions =
// *************** //
// Utility Methods //
// *************** //
/**
* set preview canvas dimensions according to the canvas element of the original image
* @private
* @param {?} img
* @return {?}
*/
function (img) {
// set preview pane dimensions
this.previewDimensions = this.calculateDimensions(img.width, img.height);
this.previewCanvas.nativeElement.width = this.previewDimensions.width;
this.previewCanvas.nativeElement.height = this.previewDimensions.height;
this.imageResizeRatio = this.previewDimensions.width / img.width;
this.imageDivStyle = {
width: this.previewDimensions.width + this.options.cropToolDimensions.width + 'px',
height: this.previewDimensions.height + this.options.cropToolDimensions.height + 'px',
'margin-left': "calc((100% - " + (this.previewDimensions.width + 10) + "px) / 2 + " + this.options.cropToolDimensions.width / 2 + "px)",
'margin-right': "calc((100% - " + (this.previewDimensions.width + 10) + "px) / 2 - " + this.options.cropToolDimensions.width / 2 + "px)",
};
this.limitsService.setPaneDimensions({ width: this.previewDimensions.width, height: this.previewDimensions.height });
};
/**
* calculate dimensions of the preview canvas
*/
/**
* calculate dimensions of the preview canvas
* @private
* @param {?} width
* @param {?} height
* @return {?}
*/
NgxDocScannerComponent.prototype.calculateDimensions = /**
* calculate dimensions of the preview canvas
* @private
* @param {?} width
* @param {?} height
* @return {?}
*/
function (width, height) {
/** @type {?} */
var ratio = width / height;
/** @type {?} */
var maxWidth = this.screenDimensions.width > this.maxPreviewWidth ?
this.maxPreviewWidth : this.screenDimensions.width - 40;
/** @type {?} */
var maxHeight = this.screenDimensions.height - 240;
/** @type {?} */
var calculated = {
width: maxWidth,
height: Math.round(maxWidth / ratio),
ratio: ratio
};
if (calculated.height > maxHeight) {
calculated.height = maxHeight;
calculated.width = Math.round(maxHeight * ratio);
}
return calculated;
};
/**
* returns a point by it's roles
* @param roles - an array of roles by which the point will be fetched
*/
/**
* returns a point by it's roles
* @private
* @param {?} roles - an array of roles by which the point will be fetched
* @return {?}
*/
NgxDocScannerComponent.prototype.getPoint = /**
* returns a point by it's roles
* @private
* @param {?} roles - an array of roles by which the point will be fetched
* @return {?}
*/
function (roles) {
var _this = this;
return this.points.find((/**
* @param {?} point
* @return {?}
*/
function (point) {
return _this.limitsService.compareArray(point.roles, roles);
}));
};
NgxDocScannerComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-doc-scanner',
template: "<div [ngStyle]=\"editorStyle\" fxLayoutAlign=\"space-around\" style=\"direction: ltr !important\">\n <div #imageContainer [ngStyle]=\"imageDivStyle\" style=\"margin: auto;\" >\n <ng-container *ngIf=\"imageLoaded && mode === 'crop'\">\n <ngx-shape-outine #shapeOutline [color]=\"options.cropToolColor\" [weight]=\"options.cropToolLineWeight\" [dimensions]=\"previewDimensions\"></ngx-shape-outine>\n <ngx-draggable-point #topLeft [pointOptions]=\"options.pointOptions\" [startPosition]=\"{x: 0, y: 0}\" [limitRoles]=\"['top', 'left']\" [container]=\"imageContainer\"></ngx-draggable-point>\n <ngx-draggable-point #topRight [pointOptions]=\"options.pointOptions\" [startPosition]=\"{x: previewDimensions.width, y: 0}\" [limitRoles]=\"['top', 'right']\" [container]=\"imageContainer\"></ngx-draggable-point>\n <ngx-draggable-point #bottomLeft [pointOptions]=\"options.pointOptions\" [startPosition]=\"{x: 0, y: previewDimensions.height}\" [limitRoles]=\"['bottom', 'left']\" [container]=\"imageContainer\"></ngx-draggable-point>\n <ngx-draggable-point #bottomRight [pointOptions]=\"options.pointOptions\" [startPosition]=\"{x: previewDimensions.width, y: previewDimensions.height}\" [limitRoles]=\"['bottom', 'right']\" [container]=\"imageContainer\"></ngx-draggable-point>\n </ng-container>\n <canvas #PreviewCanvas [ngStyle]=\"{'max-width': options.maxPreviewWidth}\" style=\"z-index: 5\" ></canvas>\n </div>\n <div class=\"editor-actions\" fxLayout=\"row\" fxLayoutAlign=\"space-around\" style=\"position: absolute; bottom: 0; width: 100vw\">\n <ng-container *ngFor=\"let button of displayedButtons\" [ngSwitch]=\"button.type\">\n <button mat-mini-fab *ngSwitchCase=\"'fab'\" [name]=\"button.name\" (click)=\"button.action()\" [color]=\"options.buttonThemeColor\">\n <mat-icon>{{button.icon}}</mat-icon>\n </button>\n <button mat-raised-button *ngSwitchCase=\"'button'\" [name]=\"button.name\" (click)=\"button.action()\" [color]=\"options.buttonThemeColor\">\n <mat-icon>{{button.icon}}</mat-icon>\n <span>{{button.text}}}</span>\n </button>\n </ng-container>\n </div>\n</div>\n\n\n",
styles: [".editor-actions{padding:12px}.editor-actions button{margin:5px}"]
}] }
];
/** @nocollapse */
NgxDocScannerComponent.ctorParameters = function () { return [
{ type: NgxOpenCVService },
{ type: LimitsService },
{ type: MatBottomSheet }
]; };
NgxDocScannerComponent.propDecorators = {
previewCanvas: [{ type: ViewChild, args: ['PreviewCanvas', { read: ElementRef },] }],
exitEditor: [{ type: Output }],
editResult: [{ type: Output }],
error: [{ type: Output }],
ready: [{ type: Output }],
processing: [{ type: Output }],
file: [{ type: Input }],
config: [{ type: Input }]
};
return NgxDocScannerComponent;
}());
export { NgxDocScannerComponent };
if (false) {
/**
* editor config object
* @type {?}
*/
NgxDocScannerComponent.prototype.options;
/**
* an array of action buttons displayed on the editor screen
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.editorButtons;
/**
* max width of the preview area
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.maxPreviewWidth;
/**
* dimensions of the image container
* @type {?}
*/
NgxDocScannerComponent.prototype.imageDivStyle;
/**
* editor div style
* @type {?}
*/
NgxDocScannerComponent.prototype.editorStyle;
/**
* state of opencv loading
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.cvState;
/**
* true after the image is loaded and preview is displayed
* @type {?}
*/
NgxDocScannerComponent.prototype.imageLoaded;
/**
* editor mode
* @type {?}
*/
NgxDocScannerComponent.prototype.mode;
/**
* filter selected by the user, returned by the filter selector bottom sheet
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.selectedFilter;
/**
* viewport dimensions
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.screenDimensions;
/**
* image dimensions
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.imageDimensions;
/**
* dimensions of the preview pane
* @type {?}
*/
NgxDocScannerComponent.prototype.previewDimensions;
/**
* ration between preview image and original
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.imageResizeRatio;
/**
* stores the original image for reset purposes
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.originalImage;
/**
* stores the edited image
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.editedImage;
/**
* stores the preview image as canvas
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.previewCanvas;
/**
* an array of points used by the crop tool
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.points;
/**
* optional binding to the exit button of the editor
* @type {?}
*/
NgxDocScannerComponent.prototype.exitEditor;
/**
* fires on edit completion
* @type {?}
*/
NgxDocScannerComponent.prototype.editResult;
/**
* emits errors, can be linked to an error handler of choice
* @type {?}
*/
NgxDocScannerComponent.prototype.error;
/**
* emits the loading status of the cv module.
* @type {?}
*/
NgxDocScannerComponent.prototype.ready;
/**
* emits true when processing is done, false when completed
* @type {?}
*/
NgxDocScannerComponent.prototype.processing;
/**
* editor configuration object
* @type {?}
*/
NgxDocScannerComponent.prototype.config;
/**
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.ngxOpenCv;
/**
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.limitsService;
/**
* @type {?}
* @private
*/
NgxDocScannerComponent.prototype.bottomSheet;
}
/**
* a class for generating configuration objects for the editor
*/
var /**
* a class for generating configuration objects for the editor
*/
ImageEditorConfig = /** @class */ (function () {
function ImageEditorConfig(options) {
var _this = this;
/**
* max dimensions of oputput image. if set to zero
*/
this.maxImageDimensions = {
width: 800,
height: 1200
};
/**
* background color of the main editor div
*/
this.editorBackgroundColor = '#fefefe';
/**
* css properties for the main editor div
*/
this.editorDimensions = {
width: '100vw',
height: '100vh'
};
/**
* css that will be added to the main div of the editor component
*/
this.extraCss = {
position: 'absolute',
top: 0,
left: 0
};
/**
* material design theme color name
*/
this.buttonThemeColor = 'accent';
/**
* icon for the button that completes the editing and emits the edited image
*/
this.exportImageIcon = 'cloud_upload';
/**
* color of the crop tool
*/
this.cropToolColor = '#3cabe2';
/**
* shape of the crop tool, can be either a rectangle or a circle
*/
this.cropToolShape = 'rect';
/**
* dimensions of the crop tool
*/
this.cropToolDimensions = {
width: 10,
height: 10
};
/**
* crop tool outline width
*/
this.cropToolLineWeight = 3;
/**
* maximum size of the preview pane
*/
this.maxPreviewWidth = 800;
if (options) {
Object.keys(options).forEach((/**
* @param {?} k