├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── angular.json ├── archive └── ngx-opencv.service.ts ├── dist ├── ngx-document-scanner │ ├── README.md │ ├── bundles │ │ ├── ngx-document-scanner.umd.js │ │ ├── ngx-document-scanner.umd.js.map │ │ ├── ngx-document-scanner.umd.min.js │ │ └── ngx-document-scanner.umd.min.js.map │ ├── esm2015 │ │ ├── lib │ │ │ ├── PrivateModels.js │ │ │ ├── PublicModels.js │ │ │ ├── components │ │ │ │ ├── draggable-point │ │ │ │ │ └── ngx-draggable-point.component.js │ │ │ │ ├── filter-menu │ │ │ │ │ └── ngx-filter-menu.component.js │ │ │ │ ├── image-editor │ │ │ │ │ └── ngx-doc-scanner.component.js │ │ │ │ └── shape-outline │ │ │ │ │ └── ngx-shape-outline.component.js │ │ │ ├── ngx-document-scanner.module.js │ │ │ └── services │ │ │ │ └── limits.service.js │ │ ├── ngx-document-scanner.js │ │ └── public_api.js │ ├── esm5 │ │ ├── lib │ │ │ ├── PrivateModels.js │ │ │ ├── PublicModels.js │ │ │ ├── components │ │ │ │ ├── draggable-point │ │ │ │ │ └── ngx-draggable-point.component.js │ │ │ │ ├── filter-menu │ │ │ │ │ └── ngx-filter-menu.component.js │ │ │ │ ├── image-editor │ │ │ │ │ └── ngx-doc-scanner.component.js │ │ │ │ └── shape-outline │ │ │ │ │ └── ngx-shape-outline.component.js │ │ │ ├── ngx-document-scanner.module.js │ │ │ └── services │ │ │ │ └── limits.service.js │ │ ├── ngx-document-scanner.js │ │ └── public_api.js │ ├── fesm2015 │ │ ├── ngx-document-scanner.js │ │ └── ngx-document-scanner.js.map │ ├── fesm5 │ │ ├── ngx-document-scanner.js │ │ └── ngx-document-scanner.js.map │ ├── lib │ │ ├── PrivateModels.d.ts │ │ ├── PublicModels.d.ts │ │ ├── components │ │ │ ├── draggable-point │ │ │ │ └── ngx-draggable-point.component.d.ts │ │ │ ├── filter-menu │ │ │ │ └── ngx-filter-menu.component.d.ts │ │ │ ├── image-editor │ │ │ │ └── ngx-doc-scanner.component.d.ts │ │ │ └── shape-outline │ │ │ │ └── ngx-shape-outline.component.d.ts │ │ ├── ngx-document-scanner.module.d.ts │ │ └── services │ │ │ └── limits.service.d.ts │ ├── ngx-document-scanner.d.ts │ ├── ngx-document-scanner.metadata.json │ ├── package.json │ └── public_api.d.ts ├── ngx-ds-demo-app │ ├── 3rdpartylicenses.txt │ ├── 404.html │ ├── assets │ │ ├── git.jpg │ │ ├── npm.jpg │ │ ├── opencv.png │ │ └── opencv │ │ │ ├── opencv.js │ │ │ └── opencv_js.wasm │ ├── favicon.ico │ ├── index.html │ ├── main.9b573c39ea09a56bb464.js │ ├── polyfills.ada13a984941328c4d6e.js │ ├── runtime.ec2944dd8b20ec099bf3.js │ └── styles.299b96676d60052e9e30.css └── ngx-opencv │ ├── README.md │ ├── bundles │ ├── ngx-opencv.umd.js │ ├── ngx-opencv.umd.js.map │ ├── ngx-opencv.umd.min.js │ └── ngx-opencv.umd.min.js.map │ ├── esm2015 │ ├── lib │ │ ├── models.js │ │ ├── ngx-open-cv.service.js │ │ └── ngx-opencv.module.js │ ├── ngx-opencv.js │ └── public_api.js │ ├── esm5 │ ├── lib │ │ ├── models.js │ │ ├── ngx-open-cv.service.js │ │ └── ngx-opencv.module.js │ ├── ngx-opencv.js │ └── public_api.js │ ├── fesm2015 │ ├── ngx-opencv.js │ └── ngx-opencv.js.map │ ├── fesm5 │ ├── ngx-opencv.js │ └── ngx-opencv.js.map │ ├── lib │ ├── models.d.ts │ ├── ngx-open-cv.service.d.ts │ └── ngx-opencv.module.d.ts │ ├── ngx-opencv.d.ts │ ├── ngx-opencv.metadata.json │ ├── package.json │ └── public_api.d.ts ├── package-lock.json ├── package.json ├── projects ├── ngx-document-scanner │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ │ ├── lib │ │ │ ├── PrivateModels.ts │ │ │ ├── PublicModels.ts │ │ │ ├── components │ │ │ │ ├── draggable-point │ │ │ │ │ ├── ngx-draggable-point.component.html │ │ │ │ │ └── ngx-draggable-point.component.ts │ │ │ │ ├── filter-menu │ │ │ │ │ ├── ngx-filter-menu.component.html │ │ │ │ │ └── ngx-filter-menu.component.ts │ │ │ │ ├── image-editor │ │ │ │ │ ├── ngx-doc-scanner.component.html │ │ │ │ │ ├── ngx-doc-scanner.component.scss │ │ │ │ │ └── ngx-doc-scanner.component.ts │ │ │ │ └── shape-outline │ │ │ │ │ ├── ngx-shape-outline.component.html │ │ │ │ │ └── ngx-shape-outline.component.ts │ │ │ ├── ngx-document-scanner.module.ts │ │ │ └── services │ │ │ │ └── limits.service.ts │ │ ├── public_api.ts │ │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ └── tslint.json ├── ngx-ds-demo-app │ ├── browserslist │ ├── karma.conf.js │ ├── src │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ ├── components │ │ │ │ ├── demo │ │ │ │ │ ├── demo.component.html │ │ │ │ │ ├── demo.component.scss │ │ │ │ │ ├── demo.component.spec.ts │ │ │ │ │ └── demo.component.ts │ │ │ │ └── side-nav │ │ │ │ │ ├── side-nav.component.html │ │ │ │ │ ├── side-nav.component.scss │ │ │ │ │ ├── side-nav.component.spec.ts │ │ │ │ │ └── side-nav.component.ts │ │ │ └── routing.module.ts │ │ ├── assets │ │ │ ├── .gitkeep │ │ │ ├── git.jpg │ │ │ ├── npm.jpg │ │ │ ├── opencv.png │ │ │ └── opencv │ │ │ │ ├── opencv.js │ │ │ │ └── opencv_js.wasm │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.css │ │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── tslint.json └── ngx-opencv │ ├── README.md │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── models.ts │ │ ├── ngx-open-cv.service.ts │ │ └── ngx-opencv.module.ts │ └── public_api.ts │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ └── tslint.json ├── src ├── app │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── components │ │ ├── draggable-point │ │ │ ├── draggable-point.component.html │ │ │ ├── draggable-point.component.scss │ │ │ ├── draggable-point.component.spec.ts │ │ │ └── draggable-point.component.ts │ │ ├── image-container │ │ │ ├── image-container.component.html │ │ │ ├── image-container.component.scss │ │ │ └── image-container.component.spec.ts │ │ ├── image-editor │ │ │ ├── image-editor.component.scss │ │ │ ├── image-editor.component.spec.ts │ │ │ └── image-editor.component.ts │ │ └── shape-outine │ │ │ ├── shape-outine.component.html │ │ │ ├── shape-outine.component.scss │ │ │ ├── shape-outine.component.spec.ts │ │ │ └── shape-outine.component.ts │ └── services │ │ ├── limits.service.spec.ts │ │ └── limits.service.ts ├── assets │ └── .gitkeep ├── browserslist ├── favicon.ico └── index.html ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /tmp 5 | /out-tsc 6 | 7 | # dependencies 8 | /node_modules 9 | 10 | # IDEs and editors 11 | /.idea 12 | .project 13 | .classpath 14 | .c9/ 15 | *.launch 16 | .settings/ 17 | *.sublime-workspace 18 | 19 | # IDE - VSCode 20 | .vscode/* 21 | !.vscode/settings.json 22 | !.vscode/tasks.json 23 | !.vscode/launch.json 24 | !.vscode/extensions.json 25 | 26 | # misc 27 | /.sass-cache 28 | /connect.lock 29 | /coverage 30 | /libpeerconnection.log 31 | npm-debug.log 32 | yarn-error.log 33 | testem.log 34 | /typings 35 | 36 | # System Files 37 | .DS_Store 38 | Thumbs.db 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 roiperlman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ngx Document Scanner 2 | 3 | An Angular component for cropping and enhancing images of documents, for implementation on a mobile or desktop app. 4 | It uses a [WASM](https://webassembly.org/) build of [OpenCV](https://opencv.org/) to manipulate images, to achieve near-native performance. 5 | Note that there are a few extra steps required to configure the component other than installing the package from npm. 6 | 7 | ## Live Demo 8 | View a live demo **[here](https://roiperlman.github.io/ngx-document-scanner)** 9 | 10 | ## Installation & Setup 11 | install the package via npm 12 | 13 | npm install ngx-document-scanner --save 14 | 15 | the UI is based on `@angular/material`, if you don't have it installed: 16 | 17 | ng add @angular/material 18 | 19 | choose 'yes' when prompted if you wish to add angular animations as it is needed for some of the components. 20 | 21 | ##### Configure OpenCV 22 | copy the opencv.js files to your assets folder (or any other folder). you can build the files yourself ([instructions on the OpenCV site](https://docs.opencv.org/3.4/d4/da1/tutorial_js_setup.html)), or download them from this package's [repository](https://github.com/roiperlman/ngx-document-scanner). 23 | both opencv.js & opencv_js.wasm need to placed in the same folder. 24 | 25 | import the package to your `app.module`. you'll need to configure the location of the open cv files. 26 | 27 | import {OpenCVConfig} from 'ngx-document-scanner'; 28 | import {NgxDocumentScannerModule} from 'ngx-document-scanner'; 29 | 30 | // set the location of the OpenCV files 31 | const openCVConfig: OpenCVConfig = { 32 | openCVDirPath: '/assets/opencv' 33 | }; 34 | 35 | @NgModule({ imports: 36 | [NgxDocumentScannerModule.forRoot(openCVConfig)], 37 | bootstrap: [AppComponent] 38 | }) 39 | export class AppModule { } 40 | 41 | ## Usage 42 | 43 | #### add component to template and bind to inputs and outputs. 44 | 45 | 53 | 54 | 55 | #### set configuration options. for example: 56 | 57 | config: DocScannerConfig = { 58 | editorBackgroundColor: '#fafafa', 59 | buttonThemeColor: 'primary', 60 | cropToolColor: '#ff4081', 61 | cropToolShape: 'circle', 62 | exportImageIcon: 'cloud_download' 63 | }; 64 | 65 | ## Component I\O 66 | ### Inputs 67 | 68 | |input|type|description| 69 | |--|--|--| 70 | | **file** | `File` | sets an image for editing | 71 | | **config** | `DocScannerConfig` | configuration object for the component. see [section](#config) dedicated to te config object. | 72 | 73 | ### Outputs 74 | 75 | |output|type|description| 76 | |--|--|--| 77 | | **error** | `EventEmitter` | fires on error | 78 | | **editResult** | `EventEmitter` | fires when the users submits the image | 79 | |**exitEditor**| `EventEmitter`| fires when the user exits the editor| 80 | |**processing**|`EventEmitter`|fires true when the editor is prcessing or loading\parsing the OpenCV module. 81 | 82 | 83 | 84 | ## Configuration Object 85 | optional configuration values that can be passed to the component. 86 | 87 | import {DocScannerConfig} from 'ngx-document-scanner' 88 | config: DocScannerConfig = { 89 | .... 90 | } 91 | 92 | | property | type | description | 93 | |--|--|--| 94 | |**buttonThemeColor** | "primary" | "warn" | "accent" | material design theme color name for the buttons on the component| 95 | |**cropToolColor**|`string`|color of the crop tool (points and connecting lines) | 96 | |**cropToolDimensions** | `{width: number; height: nubmer;}`| width and height of the crop tool points| 97 | |**cropToolLineWeight** |`number`|weight of the crop tool's connecting lines | 98 | |**cropToolShape**|`'rect' | 'circle'`|shape of the crop tool points | 99 | |**editorBackgroundColor**|`string`|background color of the main editor div | 100 | |**editorDimensions** | an object of css keys value pairs| css properties for the main editor div | 101 | |**exportImageIcon** |`string`| icon for the button that completes the editing and emits the edited image.| 102 | **extraCss**|an object of css keys value pairs|css that will be added to the main div of the editor component | 103 | |**maxImageDimensions** | `{width: number; height: nubmer;}` | max dimensions of oputput image. if set to zero will not resize the image.| 104 | |**maxPreviewWidth** | `number`|max width of the preview pane| 105 | 106 | ## Ngx-OpenCV 107 | The angular service used to load the open cv library and monitor it's state is also available as a standalone package: [NgxOpenCV](https://www.npmjs.com/ngx-opencv) 108 | 109 | ## License 110 | 111 | This project is licensed under the MIT License. 112 | 113 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-document-scanner": { 7 | "root": "projects/ngx-document-scanner", 8 | "sourceRoot": "projects/ngx-document-scanner/src", 9 | "projectType": "library", 10 | "prefix": "ngx", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-ng-packagr:build", 14 | "options": { 15 | "tsConfig": "projects/ngx-document-scanner/tsconfig.lib.json", 16 | "project": "projects/ngx-document-scanner/ng-package.json" 17 | } 18 | }, 19 | "test": { 20 | "builder": "@angular-devkit/build-angular:karma", 21 | "options": { 22 | "main": "projects/ngx-document-scanner/src/test.ts", 23 | "tsConfig": "projects/ngx-document-scanner/tsconfig.spec.json", 24 | "karmaConfig": "projects/ngx-document-scanner/karma.conf.js" 25 | } 26 | }, 27 | "lint": { 28 | "builder": "@angular-devkit/build-angular:tslint", 29 | "options": { 30 | "tsConfig": [ 31 | "projects/ngx-document-scanner/tsconfig.lib.json", 32 | "projects/ngx-document-scanner/tsconfig.spec.json" 33 | ], 34 | "exclude": [ 35 | "**/node_modules/**" 36 | ] 37 | } 38 | } 39 | } 40 | }, 41 | "ngx-ds-demo-app": { 42 | "root": "projects/ngx-ds-demo-app/", 43 | "sourceRoot": "projects/ngx-ds-demo-app/src", 44 | "projectType": "application", 45 | "prefix": "app", 46 | "schematics": {}, 47 | "architect": { 48 | "build": { 49 | "builder": "@angular-devkit/build-angular:browser", 50 | "options": { 51 | "outputPath": "dist/ngx-ds-demo-app", 52 | "index": "projects/ngx-ds-demo-app/src/index.html", 53 | "main": "projects/ngx-ds-demo-app/src/main.ts", 54 | "polyfills": "projects/ngx-ds-demo-app/src/polyfills.ts", 55 | "tsConfig": "projects/ngx-ds-demo-app/tsconfig.app.json", 56 | "assets": [ 57 | "projects/ngx-ds-demo-app/src/favicon.ico", 58 | "projects/ngx-ds-demo-app/src/assets" 59 | ], 60 | "styles": [ 61 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", 62 | "projects/ngx-ds-demo-app/src/styles.css" 63 | ], 64 | "scripts": [] 65 | }, 66 | "configurations": { 67 | "production": { 68 | "fileReplacements": [ 69 | { 70 | "replace": "projects/ngx-ds-demo-app/src/environments/environment.ts", 71 | "with": "projects/ngx-ds-demo-app/src/environments/environment.prod.ts" 72 | } 73 | ], 74 | "optimization": true, 75 | "outputHashing": "all", 76 | "sourceMap": false, 77 | "extractCss": true, 78 | "namedChunks": false, 79 | "aot": true, 80 | "extractLicenses": true, 81 | "vendorChunk": false, 82 | "buildOptimizer": true, 83 | "budgets": [ 84 | { 85 | "type": "initial", 86 | "maximumWarning": "2mb", 87 | "maximumError": "5mb" 88 | } 89 | ] 90 | } 91 | } 92 | }, 93 | "serve": { 94 | "builder": "@angular-devkit/build-angular:dev-server", 95 | "options": { 96 | "browserTarget": "ngx-ds-demo-app:build" 97 | }, 98 | "configurations": { 99 | "production": { 100 | "browserTarget": "ngx-ds-demo-app:build:production" 101 | } 102 | } 103 | }, 104 | "extract-i18n": { 105 | "builder": "@angular-devkit/build-angular:extract-i18n", 106 | "options": { 107 | "browserTarget": "ngx-ds-demo-app:build" 108 | } 109 | }, 110 | "test": { 111 | "builder": "@angular-devkit/build-angular:karma", 112 | "options": { 113 | "main": "projects/ngx-ds-demo-app/src/test.ts", 114 | "polyfills": "projects/ngx-ds-demo-app/src/polyfills.ts", 115 | "tsConfig": "projects/ngx-ds-demo-app/tsconfig.spec.json", 116 | "karmaConfig": "projects/ngx-ds-demo-app/karma.conf.js", 117 | "styles": [ 118 | "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", 119 | "projects/ngx-ds-demo-app/src/styles.css" 120 | ], 121 | "scripts": [], 122 | "assets": [ 123 | "projects/ngx-ds-demo-app/src/favicon.ico", 124 | "projects/ngx-ds-demo-app/src/assets" 125 | ] 126 | } 127 | }, 128 | "lint": { 129 | "builder": "@angular-devkit/build-angular:tslint", 130 | "options": { 131 | "tsConfig": [ 132 | "projects/ngx-ds-demo-app/tsconfig.app.json", 133 | "projects/ngx-ds-demo-app/tsconfig.spec.json" 134 | ], 135 | "exclude": [ 136 | "**/node_modules/**" 137 | ] 138 | } 139 | } 140 | } 141 | }, 142 | "ngx-opencv": { 143 | "root": "projects/ngx-opencv", 144 | "sourceRoot": "projects/ngx-opencv/src", 145 | "projectType": "library", 146 | "prefix": "ngx", 147 | "architect": { 148 | "build": { 149 | "builder": "@angular-devkit/build-ng-packagr:build", 150 | "options": { 151 | "tsConfig": "projects/ngx-opencv/tsconfig.lib.json", 152 | "project": "projects/ngx-opencv/ng-package.json" 153 | } 154 | }, 155 | "test": { 156 | "builder": "@angular-devkit/build-angular:karma", 157 | "options": { 158 | "main": "projects/ngx-opencv/src/test.ts", 159 | "tsConfig": "projects/ngx-opencv/tsconfig.spec.json", 160 | "karmaConfig": "projects/ngx-opencv/karma.conf.js" 161 | } 162 | }, 163 | "lint": { 164 | "builder": "@angular-devkit/build-angular:tslint", 165 | "options": { 166 | "tsConfig": [ 167 | "projects/ngx-opencv/tsconfig.lib.json", 168 | "projects/ngx-opencv/tsconfig.spec.json" 169 | ], 170 | "exclude": [ 171 | "**/node_modules/**" 172 | ] 173 | } 174 | } 175 | } 176 | } 177 | }, 178 | "defaultProject": "ngx-ds-demo-app", 179 | "cli": { 180 | "analytics": false 181 | } 182 | } -------------------------------------------------------------------------------- /archive/ngx-opencv.service.ts: -------------------------------------------------------------------------------- 1 | import {Inject, Injectable, InjectionToken, NgZone} from '@angular/core'; 2 | import {BehaviorSubject} from 'rxjs'; 3 | import {OpenCVConfig, OpenCVState} from '../PublicModels'; 4 | 5 | export const OpenCvConfigToken = new InjectionToken('OpenCV config object token'); 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class NgxOpenCVService { 11 | 12 | cvState = new BehaviorSubject({ 13 | ready: false, 14 | error: false, 15 | loading: true, 16 | state: 'loading' 17 | }); 18 | configModule: OpenCVConfigModule; 19 | 20 | constructor(@Inject(OpenCvConfigToken) options: OpenCVConfig, private _ngZone: NgZone) { 21 | if (!options) { 22 | options = {}; 23 | } 24 | this.configModule = this.generateConfigModule(options); 25 | this.loadOpenCv(); 26 | } 27 | 28 | /** 29 | * load the OpenCV script 30 | */ 31 | loadOpenCv() { 32 | this.cvState.next( this.newState('loading')); 33 | // create global module variable 34 | window['Module'] = this.configModule; 35 | 36 | // create script element and set attributes 37 | const script = document.createElement('script'); 38 | script.setAttribute('async', ''); 39 | script.setAttribute('type', 'text/javascript'); 40 | 41 | // listen for errors 42 | script.addEventListener('error', () => { 43 | const err = new Error('Failed to load ' + this.configModule.scriptUrl); 44 | this.cvState.next(this.newState('error')); 45 | this.cvState.error(err); 46 | }, {passive: true}); 47 | 48 | // set script url 49 | script.src = this.configModule.scriptUrl; 50 | // insert script as first script tag 51 | const node = document.getElementsByTagName('script')[0]; 52 | if (node) { 53 | node.parentNode.insertBefore(script, node); 54 | } else { 55 | document.head.appendChild(script); 56 | } 57 | } 58 | 59 | /** 60 | * generates a new state object 61 | * @param change - the new state of the module 62 | */ 63 | private newState(change: 'loading'|'ready'|'error'): OpenCVState { 64 | const newStateObj: OpenCVState = { 65 | ready: false, 66 | loading: false, 67 | error: false, 68 | state: '' 69 | }; 70 | Object.keys(newStateObj).forEach(key => { 71 | if (key !== 'state') { 72 | if (key === change) { 73 | newStateObj[key] = true; 74 | newStateObj.state = key; 75 | } else { 76 | newStateObj[key] = false; 77 | } 78 | } 79 | }); 80 | return newStateObj; 81 | } 82 | 83 | /** 84 | * generates a config module for the global Module object 85 | * @param options - configuration options 86 | */ 87 | private generateConfigModule(options: OpenCVConfig): OpenCVConfigModule { 88 | return { 89 | scriptUrl: options.openCVDirPath ? `${options.openCVDirPath}/opencv.js` : `/assets/opencv/opencv.js`, 90 | wasmBinaryFile: 'opencv_js.wasm', 91 | usingWasm: true, 92 | onRuntimeInitialized: () => { 93 | this._ngZone.run(() => { 94 | console.log('openCV Ready'); 95 | this.cvState.next(this.newState('ready')); 96 | if (options.runOnOpenCVInit) { 97 | options.runOnOpenCVInit(); 98 | } 99 | }); 100 | } 101 | }; 102 | } 103 | } 104 | 105 | /** 106 | * describes the global Module object that is used to initiate OpenCV.js 107 | */ 108 | interface OpenCVConfigModule { 109 | scriptUrl: string; 110 | wasmBinaryFile: string; 111 | usingWasm: boolean; 112 | onRuntimeInitialized: Function; 113 | } 114 | 115 | 116 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/README.md: -------------------------------------------------------------------------------- 1 | # Ngx Document Scanner 2 | 3 | An Angular component for cropping and enhancing images of documents, for implementation on a mobile or desktop app. 4 | It uses a [WASM](https://webassembly.org/) build of [OpenCV](https://opencv.org/) to manipulate images, to achieve near-native performance. 5 | Note that there are a few extra steps required to configure the component other than installing the package from npm. 6 | 7 | ## Live Demo 8 | View a live demo **[here](https://roiperlman.github.io/ngx-document-scanner)** 9 | 10 | ## Installation & Setup 11 | install the package via npm 12 | 13 | npm install ngx-document-scanner --save 14 | 15 | the UI is based on `@angular/material`, if you don't have it installed: 16 | 17 | ng add @angular/material 18 | 19 | choose 'yes' when prompted if you wish to add angular animations as it is needed for some of the components. 20 | 21 | ##### Configure OpenCV 22 | copy the opencv.js files to your assets folder (or any other folder). you can build the files yourself ([instructions on the OpenCV site](https://docs.opencv.org/3.4/d4/da1/tutorial_js_setup.html)), or download them from this package's [repository](https://github.com/roiperlman/ngx-document-scanner). 23 | both opencv.js & opencv_js.wasm need to placed in the same folder. 24 | 25 | import the package to your `app.module`. you'll need to configure the location of the open cv files. 26 | 27 | import {OpenCVConfig} from 'ngx-document-scanner'; 28 | import {NgxDocumentScannerModule} from 'ngx-document-scanner'; 29 | 30 | // set the location of the OpenCV files 31 | const openCVConfig: OpenCVConfig = { 32 | openCVDirPath: '/assets/opencv' 33 | }; 34 | 35 | @NgModule({ imports: 36 | [NgxDocumentScannerModule.forRoot(openCVConfig)], 37 | bootstrap: [AppComponent] 38 | }) 39 | export class AppModule { } 40 | 41 | ## Usage 42 | 43 | #### add component to template and bind to inputs and outputs. 44 | 45 | 49 | (editResult)="editResult($event)" 50 | (exitEditor)="exitEditor($event)" 51 | (error)="onError($event)" 52 | (processing)="editorState($event)" 53 | 54 | 55 | #### set configuration options. for example: 56 | 57 | config: DocScannerConfig = { 58 | editorBackgroundColor: '#fafafa', 59 | buttonThemeColor: 'primary', 60 | cropToolColor: '#ff4081', 61 | cropToolShape: 'circle', 62 | exportImageIcon: 'cloud_download' 63 | }; 64 | 65 | ## Component I\O 66 | ### Inputs 67 | 68 | |input|type|description| 69 | |--|--|--| 70 | | **file** | `File` | sets an image for editing | 71 | | **config** | `DocScannerConfig` | configuration object for the component. see [section](#config) dedicated to te config object. | 72 | 73 | ### Outputs 74 | 75 | |output|type|description| 76 | |--|--|--| 77 | | **error** | `EventEmitter` | fires on error | 78 | | **editResult** | `EventEmitter` | fires when the users submits the image | 79 | |**exitEditor**| `EventEmitter`| fires when the user exits the editor| 80 | |**processing**|`EventEmitter`|fires true when the editor is prcessing or loading\parsing the OpenCV module. 81 | 82 | 83 | 84 | ## Configuration Object 85 | optional configuration values that can be passed to the component. 86 | 87 | import {DocScannerConfig} form 'ngx-document-scanner' 88 | config: DocScannerConfig = { 89 | .... 90 | } 91 | 92 | | property | type | description | 93 | |--|--|--| 94 | |**buttonThemeColor** | "primary" | "warn" | "accent" | material design theme color name for the buttons on the component| 95 | |**cropToolColor**|`string`|color of the crop tool (points and connecting lines) | 96 | |**cropToolDimensions** | `{width: number; height: nubmer;}`| width and height of the crop tool points| 97 | |**cropToolLineWeight** |`number`|weight of the crop tool's connecting lines | 98 | |**cropToolShape**|`'rect' | 'circle'`|shape of the crop tool points | 99 | |**editorBackgroundColor**|`string`|background color of the main editor div | 100 | |**editorDimensions** | an object of css keys value pairs| css properties for the main editor div | 101 | |**exportImageIcon** |`string`| icon for the button that completes the editing and emits the edited image.| 102 | **extraCss**|an object of css keys value pairs|css that will be added to the main div of the editor component | 103 | |**maxImageDimensions** | `{width: number; height: nubmer;}` | max dimensions of oputput image. if set to zero will not resize the image.| 104 | |**maxPreviewWidth** | `number`|max width of the preview pane| 105 | 106 | ## Ngx-OpenCV 107 | The angular service used to load the open cv library and monitor it's state is also available as a standalone package: [NgxOpenCV](https://www.npmjs.com/ngx-opencv) 108 | 109 | ## License 110 | 111 | This project is licensed under the MIT License. 112 | 113 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/esm2015/lib/PrivateModels.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: lib/PrivateModels.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /** 7 | * @record 8 | */ 9 | export function EditorActionButton() { } 10 | if (false) { 11 | /** @type {?} */ 12 | EditorActionButton.prototype.name; 13 | /** @type {?|undefined} */ 14 | EditorActionButton.prototype.type; 15 | /** @type {?} */ 16 | EditorActionButton.prototype.icon; 17 | /** @type {?} */ 18 | EditorActionButton.prototype.action; 19 | /** @type {?|undefined} */ 20 | EditorActionButton.prototype.text; 21 | /** @type {?|undefined} */ 22 | EditorActionButton.prototype.mode; 23 | } 24 | /** 25 | * @record 26 | */ 27 | export function DraggablePointConfig() { } 28 | if (false) { 29 | /** @type {?|undefined} */ 30 | DraggablePointConfig.prototype.width; 31 | /** @type {?|undefined} */ 32 | DraggablePointConfig.prototype.height; 33 | /** @type {?|undefined} */ 34 | DraggablePointConfig.prototype.color; 35 | /** @type {?|undefined} */ 36 | DraggablePointConfig.prototype.shape; 37 | /** @type {?|undefined} */ 38 | DraggablePointConfig.prototype.limitRoles; 39 | /** @type {?|undefined} */ 40 | DraggablePointConfig.prototype.startPosition; 41 | } 42 | /** 43 | * describes a position on a 2d pane 44 | * @record 45 | */ 46 | export function XYPosition() { } 47 | if (false) { 48 | /** @type {?} */ 49 | XYPosition.prototype.x; 50 | /** @type {?} */ 51 | XYPosition.prototype.y; 52 | } 53 | /** 54 | * describes o draggable point config options 55 | * @record 56 | */ 57 | export function PointOptions() { } 58 | if (false) { 59 | /** @type {?} */ 60 | PointOptions.prototype.width; 61 | /** @type {?} */ 62 | PointOptions.prototype.height; 63 | /** @type {?} */ 64 | PointOptions.prototype.color; 65 | /** @type {?} */ 66 | PointOptions.prototype.shape; 67 | } 68 | /** 69 | * @record 70 | */ 71 | export function LimitException() { } 72 | if (false) { 73 | /** @type {?} */ 74 | LimitException.prototype.exceeds; 75 | /** @type {?} */ 76 | LimitException.prototype.resetCoefficients; 77 | /** @type {?} */ 78 | LimitException.prototype.resetCoordinates; 79 | } 80 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHJpdmF0ZU1vZGVscy5qcyIsInNvdXJjZVJvb3QiOiJuZzovL25neC1kb2N1bWVudC1zY2FubmVyLyIsInNvdXJjZXMiOlsibGliL1ByaXZhdGVNb2RlbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFLQSx3Q0FPQzs7O0lBTkMsa0NBQWE7O0lBQ2Isa0NBQXdCOztJQUN4QixrQ0FBYTs7SUFDYixvQ0FBaUI7O0lBQ2pCLGtDQUFjOztJQUNkLGtDQUF3Qjs7Ozs7QUFRMUIsMENBT0M7OztJQU5DLHFDQUFlOztJQUNmLHNDQUFnQjs7SUFDaEIscUNBQWU7O0lBQ2YscUNBQTBCOztJQUMxQiwwQ0FBd0I7O0lBQ3hCLDZDQUEyQjs7Ozs7O0FBTTdCLGdDQUdDOzs7SUFGQyx1QkFBVTs7SUFDVix1QkFBVTs7Ozs7O0FBTVosa0NBS0M7OztJQUpDLDZCQUFjOztJQUNkLDhCQUFlOztJQUNmLDZCQUFjOztJQUNkLDZCQUFrQjs7Ozs7QUFHcEIsb0NBVUM7OztJQVRDLGlDQUFpQjs7SUFDakIsMkNBR0U7O0lBQ0YsMENBR0UiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGRlc2NyaWJlcyBhbiBlZGl0b3IgYnV0dG9uXG4gKi9cbmltcG9ydCB7Um9sZXNBcnJheX0gZnJvbSAnLi9zZXJ2aWNlcy9saW1pdHMuc2VydmljZSc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRWRpdG9yQWN0aW9uQnV0dG9uIHtcbiAgbmFtZTogc3RyaW5nO1xuICB0eXBlPzogJ2J1dHRvbicgfCAnZmFiJztcbiAgaWNvbjogc3RyaW5nO1xuICBhY3Rpb246IEZ1bmN0aW9uO1xuICB0ZXh0Pzogc3RyaW5nO1xuICBtb2RlPzogJ2Nyb3AnIHwgJ2NvbG9yJztcbn1cblxuLyoqXG4gKiBhIHN0cmluZyBkZXNjcmliaW5nIHRoZSBzaGFwZSBvZiBhIGRyYWdnYWJsZSBwb2ludFxuICovXG5leHBvcnQgdHlwZSBQb2ludFNoYXBlID0gJ3JlY3QnIHwgJ2NpcmNsZSc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRHJhZ2dhYmxlUG9pbnRDb25maWcge1xuICB3aWR0aD86IG51bWJlcjtcbiAgaGVpZ2h0PzogbnVtYmVyO1xuICBjb2xvcj86IHN0cmluZztcbiAgc2hhcGU/OiAncmVjdCcgfCAnY2lyY2xlJztcbiAgbGltaXRSb2xlcz86IFJvbGVzQXJyYXk7XG4gIHN0YXJ0UG9zaXRpb24/OiBYWVBvc2l0aW9uO1xufVxuXG4vKipcbiAqIGRlc2NyaWJlcyBhIHBvc2l0aW9uIG9uIGEgMmQgcGFuZVxuICovXG5leHBvcnQgaW50ZXJmYWNlIFhZUG9zaXRpb24ge1xuICB4OiBudW1iZXI7XG4gIHk6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBkZXNjcmliZXMgbyBkcmFnZ2FibGUgcG9pbnQgY29uZmlnIG9wdGlvbnNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBQb2ludE9wdGlvbnMge1xuICB3aWR0aDogbnVtYmVyO1xuICBoZWlnaHQ6IG51bWJlcjtcbiAgY29sb3I6IHN0cmluZztcbiAgc2hhcGU6IFBvaW50U2hhcGU7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTGltaXRFeGNlcHRpb24ge1xuICBleGNlZWRzOiBib29sZWFuO1xuICByZXNldENvZWZmaWNpZW50czoge1xuICAgIHg6IDEgfCAwIHwgLTFcbiAgICB5OiAxIHwgMCB8IC0xXG4gIH07XG4gIHJlc2V0Q29vcmRpbmF0ZXM6IHtcbiAgICB4OiBudW1iZXI7XG4gICAgeTogbnVtYmVyO1xuICB9O1xufVxuIl19 -------------------------------------------------------------------------------- /dist/ngx-document-scanner/esm2015/lib/PublicModels.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: lib/PublicModels.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /** 7 | * @record 8 | */ 9 | export function OpenCVState() { } 10 | if (false) { 11 | /** @type {?} */ 12 | OpenCVState.prototype.ready; 13 | /** @type {?} */ 14 | OpenCVState.prototype.loading; 15 | /** @type {?} */ 16 | OpenCVState.prototype.error; 17 | /** @type {?} */ 18 | OpenCVState.prototype.state; 19 | } 20 | /** 21 | * describes an object with width and height properties 22 | * @record 23 | */ 24 | export function ImageDimensions() { } 25 | if (false) { 26 | /** @type {?} */ 27 | ImageDimensions.prototype.width; 28 | /** @type {?} */ 29 | ImageDimensions.prototype.height; 30 | } 31 | /** 32 | * describes a configuration object for the editor 33 | * @record 34 | */ 35 | export function DocScannerConfig() { } 36 | if (false) { 37 | /** 38 | * max dimensions of output image. if set to zero will not resize the image 39 | * @type {?|undefined} 40 | */ 41 | DocScannerConfig.prototype.maxImageDimensions; 42 | /** 43 | * background color of the main editor div 44 | * @type {?|undefined} 45 | */ 46 | DocScannerConfig.prototype.editorBackgroundColor; 47 | /** 48 | * css properties for the main editor div 49 | * @type {?|undefined} 50 | */ 51 | DocScannerConfig.prototype.editorDimensions; 52 | /** 53 | * css that will be added to the main div of the editor component 54 | * @type {?|undefined} 55 | */ 56 | DocScannerConfig.prototype.extraCss; 57 | /** 58 | * material design theme color name 59 | * @type {?|undefined} 60 | */ 61 | DocScannerConfig.prototype.buttonThemeColor; 62 | /** 63 | * icon for the button that completes the editing and emits the edited image 64 | * @type {?|undefined} 65 | */ 66 | DocScannerConfig.prototype.exportImageIcon; 67 | /** 68 | * color of the crop tool (points and connecting lines) 69 | * @type {?|undefined} 70 | */ 71 | DocScannerConfig.prototype.cropToolColor; 72 | /** 73 | * shape of the crop tool points 74 | * @type {?|undefined} 75 | */ 76 | DocScannerConfig.prototype.cropToolShape; 77 | /** 78 | * width and height of the crop tool points 79 | * @type {?|undefined} 80 | */ 81 | DocScannerConfig.prototype.cropToolDimensions; 82 | /** 83 | * weight of the crop tool's connecting lines 84 | * @type {?|undefined} 85 | */ 86 | DocScannerConfig.prototype.cropToolLineWeight; 87 | /** 88 | * max width of the preview pane 89 | * @type {?|undefined} 90 | */ 91 | DocScannerConfig.prototype.maxPreviewWidth; 92 | } 93 | /** 94 | * describes a configuration object for the OpenCV service 95 | * @record 96 | */ 97 | export function OpenCVConfig() { } 98 | if (false) { 99 | /** 100 | * path to the directory containing the OpenCV files, in the form of '/path/to/' 101 | * directory must contain the the following files: 102 | * -- 103 | * ----opencv.js 104 | * ----opencv_js.wasm 105 | * @type {?|undefined} 106 | */ 107 | OpenCVConfig.prototype.openCVDirPath; 108 | /** 109 | * additional callback that will run when OpenCv has finished loading and parsing 110 | * @type {?|undefined} 111 | */ 112 | OpenCVConfig.prototype.runOnOpenCVInit; 113 | } 114 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHVibGljTW9kZWxzLmpzIiwic291cmNlUm9vdCI6Im5nOi8vbmd4LWRvY3VtZW50LXNjYW5uZXIvIiwic291cmNlcyI6WyJsaWIvUHVibGljTW9kZWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBS0EsaUNBS0M7OztJQUpDLDRCQUFlOztJQUNmLDhCQUFpQjs7SUFDakIsNEJBQWU7O0lBQ2YsNEJBQWM7Ozs7OztBQU1oQixxQ0FHQzs7O0lBRkMsZ0NBQWM7O0lBQ2QsaUNBQWU7Ozs7OztBQU1qQixzQ0E2Q0M7Ozs7OztJQXpDQyw4Q0FBcUM7Ozs7O0lBSXJDLGlEQUErQjs7Ozs7SUFJL0IsNENBQXNEOzs7OztJQUl0RCxvQ0FBOEM7Ozs7O0lBSTlDLDRDQUFpRDs7Ozs7SUFJakQsMkNBQXlCOzs7OztJQUl6Qix5Q0FBdUI7Ozs7O0lBSXZCLHlDQUEyQjs7Ozs7SUFJM0IsOENBQXFDOzs7OztJQUlyQyw4Q0FBNEI7Ozs7O0lBSTVCLDJDQUF5Qjs7Ozs7O0FBTTNCLGtDQWFDOzs7Ozs7Ozs7O0lBTEMscUNBQXVCOzs7OztJQUl2Qix1Q0FBMkIiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGRlc2NyaWJlcyBhIHN0YXRlIG9iamVjdCBmb3IgdGhlIE9wZW5DViBtb2R1bGVcbiAqL1xuaW1wb3J0IHtQb2ludFNoYXBlfSBmcm9tICcuL1ByaXZhdGVNb2RlbHMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE9wZW5DVlN0YXRlIHtcbiAgcmVhZHk6IGJvb2xlYW47XG4gIGxvYWRpbmc6IGJvb2xlYW47XG4gIGVycm9yOiBib29sZWFuO1xuICBzdGF0ZTogc3RyaW5nO1xufVxuXG4vKipcbiAqIGRlc2NyaWJlcyBhbiBvYmplY3Qgd2l0aCB3aWR0aCBhbmQgaGVpZ2h0IHByb3BlcnRpZXNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbWFnZURpbWVuc2lvbnMge1xuICB3aWR0aDogbnVtYmVyO1xuICBoZWlnaHQ6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBkZXNjcmliZXMgYSBjb25maWd1cmF0aW9uIG9iamVjdCBmb3IgdGhlIGVkaXRvclxuICovXG5leHBvcnQgaW50ZXJmYWNlIERvY1NjYW5uZXJDb25maWcge1xuICAvKipcbiAgICogbWF4IGRpbWVuc2lvbnMgb2Ygb3V0cHV0IGltYWdlLiBpZiBzZXQgdG8gemVybyB3aWxsIG5vdCByZXNpemUgdGhlIGltYWdlXG4gICAqL1xuICBtYXhJbWFnZURpbWVuc2lvbnM/OiBJbWFnZURpbWVuc2lvbnM7XG4gIC8qKlxuICAgKiBiYWNrZ3JvdW5kIGNvbG9yIG9mIHRoZSBtYWluIGVkaXRvciBkaXZcbiAgICovXG4gIGVkaXRvckJhY2tncm91bmRDb2xvcj86IHN0cmluZztcbiAgLyoqXG4gICAqIGNzcyBwcm9wZXJ0aWVzIGZvciB0aGUgbWFpbiBlZGl0b3IgZGl2XG4gICAqL1xuICBlZGl0b3JEaW1lbnNpb25zPzogeyB3aWR0aDogc3RyaW5nOyBoZWlnaHQ6IHN0cmluZzsgfTtcbiAgLyoqXG4gICAqIGNzcyB0aGF0IHdpbGwgYmUgYWRkZWQgdG8gdGhlIG1haW4gZGl2IG9mIHRoZSBlZGl0b3IgY29tcG9uZW50XG4gICAqL1xuICBleHRyYUNzcz86IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIH07XG4gIC8qKlxuICAgKiBtYXRlcmlhbCBkZXNpZ24gdGhlbWUgY29sb3IgbmFtZVxuICAgKi9cbiAgYnV0dG9uVGhlbWVDb2xvcj86ICdwcmltYXJ5JyB8ICd3YXJuJyB8ICdhY2NlbnQnO1xuICAvKipcbiAgICogaWNvbiBmb3IgdGhlIGJ1dHRvbiB0aGF0IGNvbXBsZXRlcyB0aGUgZWRpdGluZyBhbmQgZW1pdHMgdGhlIGVkaXRlZCBpbWFnZVxuICAgKi9cbiAgZXhwb3J0SW1hZ2VJY29uPzogc3RyaW5nO1xuICAvKipcbiAgICogY29sb3Igb2YgdGhlIGNyb3AgdG9vbCAocG9pbnRzIGFuZCBjb25uZWN0aW5nIGxpbmVzKVxuICAgKi9cbiAgY3JvcFRvb2xDb2xvcj86IHN0cmluZztcbiAgLyoqXG4gICAqIHNoYXBlIG9mIHRoZSBjcm9wIHRvb2wgcG9pbnRzXG4gICAqL1xuICBjcm9wVG9vbFNoYXBlPzogUG9pbnRTaGFwZTtcbiAgLyoqXG4gICAqIHdpZHRoIGFuZCBoZWlnaHQgb2YgdGhlIGNyb3AgdG9vbCBwb2ludHNcbiAgICovXG4gIGNyb3BUb29sRGltZW5zaW9ucz86IEltYWdlRGltZW5zaW9ucztcbiAgLyoqXG4gICAqIHdlaWdodCBvZiB0aGUgY3JvcCB0b29sJ3MgY29ubmVjdGluZyBsaW5lc1xuICAgKi9cbiAgY3JvcFRvb2xMaW5lV2VpZ2h0PzogbnVtYmVyO1xuICAvKipcbiAgICogbWF4IHdpZHRoIG9mIHRoZSBwcmV2aWV3IHBhbmVcbiAgICovXG4gIG1heFByZXZpZXdXaWR0aD86IG51bWJlcjtcbn1cblxuLyoqXG4gKiBkZXNjcmliZXMgYSBjb25maWd1cmF0aW9uIG9iamVjdCBmb3IgdGhlIE9wZW5DViBzZXJ2aWNlXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgT3BlbkNWQ29uZmlnIHtcbiAgLyoqXG4gICAqIHBhdGggdG8gdGhlIGRpcmVjdG9yeSBjb250YWluaW5nIHRoZSBPcGVuQ1YgZmlsZXMsIGluIHRoZSBmb3JtIG9mICcvcGF0aC90by88b3BlbmN2IGRpcmVjdG9yeT4nXG4gICAqIGRpcmVjdG9yeSBtdXN0IGNvbnRhaW4gdGhlIHRoZSBmb2xsb3dpbmcgZmlsZXM6XG4gICAqIC0tPE9wZW5DdkRpcj5cbiAgICogLS0tLW9wZW5jdi5qc1xuICAgKiAtLS0tb3BlbmN2X2pzLndhc21cbiAgICovXG4gIG9wZW5DVkRpclBhdGg/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBhZGRpdGlvbmFsIGNhbGxiYWNrIHRoYXQgd2lsbCBydW4gd2hlbiBPcGVuQ3YgaGFzIGZpbmlzaGVkIGxvYWRpbmcgYW5kIHBhcnNpbmdcbiAgICovXG4gIHJ1bk9uT3BlbkNWSW5pdD86IEZ1bmN0aW9uO1xufVxuIl19 -------------------------------------------------------------------------------- /dist/ngx-document-scanner/esm2015/ngx-document-scanner.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: ngx-document-scanner.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /** 7 | * Generated bundle index. Do not edit. 8 | */ 9 | export { NgxDocumentScannerModule, NgxDocScannerComponent } from './public_api'; 10 | export { NgxDraggablePointComponent as ɵa } from './lib/components/draggable-point/ngx-draggable-point.component'; 11 | export { NgxFilterMenuComponent as ɵc } from './lib/components/filter-menu/ngx-filter-menu.component'; 12 | export { NgxShapeOutlineComponent as ɵd } from './lib/components/shape-outline/ngx-shape-outline.component'; 13 | export { LimitsService as ɵb } from './lib/services/limits.service'; 14 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWRvY3VtZW50LXNjYW5uZXIuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9uZ3gtZG9jdW1lbnQtc2Nhbm5lci8iLCJzb3VyY2VzIjpbIm5neC1kb2N1bWVudC1zY2FubmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBSUEsaUVBQWMsY0FBYyxDQUFDO0FBRTdCLE9BQU8sRUFBQywwQkFBMEIsSUFBSSxFQUFFLEVBQUMsTUFBTSxnRUFBZ0UsQ0FBQztBQUNoSCxPQUFPLEVBQUMsc0JBQXNCLElBQUksRUFBRSxFQUFDLE1BQU0sd0RBQXdELENBQUM7QUFDcEcsT0FBTyxFQUFDLHdCQUF3QixJQUFJLEVBQUUsRUFBQyxNQUFNLDREQUE0RCxDQUFDO0FBQzFHLE9BQU8sRUFBQyxhQUFhLElBQUksRUFBRSxFQUFDLE1BQU0sK0JBQStCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEdlbmVyYXRlZCBidW5kbGUgaW5kZXguIERvIG5vdCBlZGl0LlxuICovXG5cbmV4cG9ydCAqIGZyb20gJy4vcHVibGljX2FwaSc7XG5cbmV4cG9ydCB7Tmd4RHJhZ2dhYmxlUG9pbnRDb21wb25lbnQgYXMgybVhfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL2RyYWdnYWJsZS1wb2ludC9uZ3gtZHJhZ2dhYmxlLXBvaW50LmNvbXBvbmVudCc7XG5leHBvcnQge05neEZpbHRlck1lbnVDb21wb25lbnQgYXMgybVjfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL2ZpbHRlci1tZW51L25neC1maWx0ZXItbWVudS5jb21wb25lbnQnO1xuZXhwb3J0IHtOZ3hTaGFwZU91dGxpbmVDb21wb25lbnQgYXMgybVkfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL3NoYXBlLW91dGxpbmUvbmd4LXNoYXBlLW91dGxpbmUuY29tcG9uZW50JztcbmV4cG9ydCB7TGltaXRzU2VydmljZSBhcyDJtWJ9IGZyb20gJy4vbGliL3NlcnZpY2VzL2xpbWl0cy5zZXJ2aWNlJzsiXX0= -------------------------------------------------------------------------------- /dist/ngx-document-scanner/esm2015/public_api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: public_api.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /* 7 | * Public API Surface of ngx-document-scanner 8 | */ 9 | export { NgxDocumentScannerModule } from './lib/ngx-document-scanner.module'; 10 | export { NgxDocScannerComponent } from './lib/components/image-editor/ngx-doc-scanner.component'; 11 | export {} from './lib/PublicModels'; 12 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljX2FwaS5qcyIsInNvdXJjZVJvb3QiOiJuZzovL25neC1kb2N1bWVudC1zY2FubmVyLyIsInNvdXJjZXMiOlsicHVibGljX2FwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7OztBQUlBLHlDQUFjLG1DQUFtQyxDQUFDO0FBQ2xELHVDQUFjLHlEQUF5RCxDQUFDO0FBQ3hFLGVBQWMsb0JBQW9CLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogUHVibGljIEFQSSBTdXJmYWNlIG9mIG5neC1kb2N1bWVudC1zY2FubmVyXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9saWIvbmd4LWRvY3VtZW50LXNjYW5uZXIubW9kdWxlJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbXBvbmVudHMvaW1hZ2UtZWRpdG9yL25neC1kb2Mtc2Nhbm5lci5jb21wb25lbnQnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvUHVibGljTW9kZWxzJztcbiJdfQ== -------------------------------------------------------------------------------- /dist/ngx-document-scanner/esm5/lib/PrivateModels.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: lib/PrivateModels.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /** 7 | * @record 8 | */ 9 | export function EditorActionButton() { } 10 | if (false) { 11 | /** @type {?} */ 12 | EditorActionButton.prototype.name; 13 | /** @type {?|undefined} */ 14 | EditorActionButton.prototype.type; 15 | /** @type {?} */ 16 | EditorActionButton.prototype.icon; 17 | /** @type {?} */ 18 | EditorActionButton.prototype.action; 19 | /** @type {?|undefined} */ 20 | EditorActionButton.prototype.text; 21 | /** @type {?|undefined} */ 22 | EditorActionButton.prototype.mode; 23 | } 24 | /** 25 | * @record 26 | */ 27 | export function DraggablePointConfig() { } 28 | if (false) { 29 | /** @type {?|undefined} */ 30 | DraggablePointConfig.prototype.width; 31 | /** @type {?|undefined} */ 32 | DraggablePointConfig.prototype.height; 33 | /** @type {?|undefined} */ 34 | DraggablePointConfig.prototype.color; 35 | /** @type {?|undefined} */ 36 | DraggablePointConfig.prototype.shape; 37 | /** @type {?|undefined} */ 38 | DraggablePointConfig.prototype.limitRoles; 39 | /** @type {?|undefined} */ 40 | DraggablePointConfig.prototype.startPosition; 41 | } 42 | /** 43 | * describes a position on a 2d pane 44 | * @record 45 | */ 46 | export function XYPosition() { } 47 | if (false) { 48 | /** @type {?} */ 49 | XYPosition.prototype.x; 50 | /** @type {?} */ 51 | XYPosition.prototype.y; 52 | } 53 | /** 54 | * describes o draggable point config options 55 | * @record 56 | */ 57 | export function PointOptions() { } 58 | if (false) { 59 | /** @type {?} */ 60 | PointOptions.prototype.width; 61 | /** @type {?} */ 62 | PointOptions.prototype.height; 63 | /** @type {?} */ 64 | PointOptions.prototype.color; 65 | /** @type {?} */ 66 | PointOptions.prototype.shape; 67 | } 68 | /** 69 | * @record 70 | */ 71 | export function LimitException() { } 72 | if (false) { 73 | /** @type {?} */ 74 | LimitException.prototype.exceeds; 75 | /** @type {?} */ 76 | LimitException.prototype.resetCoefficients; 77 | /** @type {?} */ 78 | LimitException.prototype.resetCoordinates; 79 | } 80 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHJpdmF0ZU1vZGVscy5qcyIsInNvdXJjZVJvb3QiOiJuZzovL25neC1kb2N1bWVudC1zY2FubmVyLyIsInNvdXJjZXMiOlsibGliL1ByaXZhdGVNb2RlbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFLQSx3Q0FPQzs7O0lBTkMsa0NBQWE7O0lBQ2Isa0NBQXdCOztJQUN4QixrQ0FBYTs7SUFDYixvQ0FBaUI7O0lBQ2pCLGtDQUFjOztJQUNkLGtDQUF3Qjs7Ozs7QUFRMUIsMENBT0M7OztJQU5DLHFDQUFlOztJQUNmLHNDQUFnQjs7SUFDaEIscUNBQWU7O0lBQ2YscUNBQTBCOztJQUMxQiwwQ0FBd0I7O0lBQ3hCLDZDQUEyQjs7Ozs7O0FBTTdCLGdDQUdDOzs7SUFGQyx1QkFBVTs7SUFDVix1QkFBVTs7Ozs7O0FBTVosa0NBS0M7OztJQUpDLDZCQUFjOztJQUNkLDhCQUFlOztJQUNmLDZCQUFjOztJQUNkLDZCQUFrQjs7Ozs7QUFHcEIsb0NBVUM7OztJQVRDLGlDQUFpQjs7SUFDakIsMkNBR0U7O0lBQ0YsMENBR0UiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGRlc2NyaWJlcyBhbiBlZGl0b3IgYnV0dG9uXG4gKi9cbmltcG9ydCB7Um9sZXNBcnJheX0gZnJvbSAnLi9zZXJ2aWNlcy9saW1pdHMuc2VydmljZSc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRWRpdG9yQWN0aW9uQnV0dG9uIHtcbiAgbmFtZTogc3RyaW5nO1xuICB0eXBlPzogJ2J1dHRvbicgfCAnZmFiJztcbiAgaWNvbjogc3RyaW5nO1xuICBhY3Rpb246IEZ1bmN0aW9uO1xuICB0ZXh0Pzogc3RyaW5nO1xuICBtb2RlPzogJ2Nyb3AnIHwgJ2NvbG9yJztcbn1cblxuLyoqXG4gKiBhIHN0cmluZyBkZXNjcmliaW5nIHRoZSBzaGFwZSBvZiBhIGRyYWdnYWJsZSBwb2ludFxuICovXG5leHBvcnQgdHlwZSBQb2ludFNoYXBlID0gJ3JlY3QnIHwgJ2NpcmNsZSc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRHJhZ2dhYmxlUG9pbnRDb25maWcge1xuICB3aWR0aD86IG51bWJlcjtcbiAgaGVpZ2h0PzogbnVtYmVyO1xuICBjb2xvcj86IHN0cmluZztcbiAgc2hhcGU/OiAncmVjdCcgfCAnY2lyY2xlJztcbiAgbGltaXRSb2xlcz86IFJvbGVzQXJyYXk7XG4gIHN0YXJ0UG9zaXRpb24/OiBYWVBvc2l0aW9uO1xufVxuXG4vKipcbiAqIGRlc2NyaWJlcyBhIHBvc2l0aW9uIG9uIGEgMmQgcGFuZVxuICovXG5leHBvcnQgaW50ZXJmYWNlIFhZUG9zaXRpb24ge1xuICB4OiBudW1iZXI7XG4gIHk6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBkZXNjcmliZXMgbyBkcmFnZ2FibGUgcG9pbnQgY29uZmlnIG9wdGlvbnNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBQb2ludE9wdGlvbnMge1xuICB3aWR0aDogbnVtYmVyO1xuICBoZWlnaHQ6IG51bWJlcjtcbiAgY29sb3I6IHN0cmluZztcbiAgc2hhcGU6IFBvaW50U2hhcGU7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTGltaXRFeGNlcHRpb24ge1xuICBleGNlZWRzOiBib29sZWFuO1xuICByZXNldENvZWZmaWNpZW50czoge1xuICAgIHg6IDEgfCAwIHwgLTFcbiAgICB5OiAxIHwgMCB8IC0xXG4gIH07XG4gIHJlc2V0Q29vcmRpbmF0ZXM6IHtcbiAgICB4OiBudW1iZXI7XG4gICAgeTogbnVtYmVyO1xuICB9O1xufVxuIl19 -------------------------------------------------------------------------------- /dist/ngx-document-scanner/esm5/lib/PublicModels.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: lib/PublicModels.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /** 7 | * @record 8 | */ 9 | export function OpenCVState() { } 10 | if (false) { 11 | /** @type {?} */ 12 | OpenCVState.prototype.ready; 13 | /** @type {?} */ 14 | OpenCVState.prototype.loading; 15 | /** @type {?} */ 16 | OpenCVState.prototype.error; 17 | /** @type {?} */ 18 | OpenCVState.prototype.state; 19 | } 20 | /** 21 | * describes an object with width and height properties 22 | * @record 23 | */ 24 | export function ImageDimensions() { } 25 | if (false) { 26 | /** @type {?} */ 27 | ImageDimensions.prototype.width; 28 | /** @type {?} */ 29 | ImageDimensions.prototype.height; 30 | } 31 | /** 32 | * describes a configuration object for the editor 33 | * @record 34 | */ 35 | export function DocScannerConfig() { } 36 | if (false) { 37 | /** 38 | * max dimensions of output image. if set to zero will not resize the image 39 | * @type {?|undefined} 40 | */ 41 | DocScannerConfig.prototype.maxImageDimensions; 42 | /** 43 | * background color of the main editor div 44 | * @type {?|undefined} 45 | */ 46 | DocScannerConfig.prototype.editorBackgroundColor; 47 | /** 48 | * css properties for the main editor div 49 | * @type {?|undefined} 50 | */ 51 | DocScannerConfig.prototype.editorDimensions; 52 | /** 53 | * css that will be added to the main div of the editor component 54 | * @type {?|undefined} 55 | */ 56 | DocScannerConfig.prototype.extraCss; 57 | /** 58 | * material design theme color name 59 | * @type {?|undefined} 60 | */ 61 | DocScannerConfig.prototype.buttonThemeColor; 62 | /** 63 | * icon for the button that completes the editing and emits the edited image 64 | * @type {?|undefined} 65 | */ 66 | DocScannerConfig.prototype.exportImageIcon; 67 | /** 68 | * color of the crop tool (points and connecting lines) 69 | * @type {?|undefined} 70 | */ 71 | DocScannerConfig.prototype.cropToolColor; 72 | /** 73 | * shape of the crop tool points 74 | * @type {?|undefined} 75 | */ 76 | DocScannerConfig.prototype.cropToolShape; 77 | /** 78 | * width and height of the crop tool points 79 | * @type {?|undefined} 80 | */ 81 | DocScannerConfig.prototype.cropToolDimensions; 82 | /** 83 | * weight of the crop tool's connecting lines 84 | * @type {?|undefined} 85 | */ 86 | DocScannerConfig.prototype.cropToolLineWeight; 87 | /** 88 | * max width of the preview pane 89 | * @type {?|undefined} 90 | */ 91 | DocScannerConfig.prototype.maxPreviewWidth; 92 | } 93 | /** 94 | * describes a configuration object for the OpenCV service 95 | * @record 96 | */ 97 | export function OpenCVConfig() { } 98 | if (false) { 99 | /** 100 | * path to the directory containing the OpenCV files, in the form of '/path/to/' 101 | * directory must contain the the following files: 102 | * -- 103 | * ----opencv.js 104 | * ----opencv_js.wasm 105 | * @type {?|undefined} 106 | */ 107 | OpenCVConfig.prototype.openCVDirPath; 108 | /** 109 | * additional callback that will run when OpenCv has finished loading and parsing 110 | * @type {?|undefined} 111 | */ 112 | OpenCVConfig.prototype.runOnOpenCVInit; 113 | } 114 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUHVibGljTW9kZWxzLmpzIiwic291cmNlUm9vdCI6Im5nOi8vbmd4LWRvY3VtZW50LXNjYW5uZXIvIiwic291cmNlcyI6WyJsaWIvUHVibGljTW9kZWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBS0EsaUNBS0M7OztJQUpDLDRCQUFlOztJQUNmLDhCQUFpQjs7SUFDakIsNEJBQWU7O0lBQ2YsNEJBQWM7Ozs7OztBQU1oQixxQ0FHQzs7O0lBRkMsZ0NBQWM7O0lBQ2QsaUNBQWU7Ozs7OztBQU1qQixzQ0E2Q0M7Ozs7OztJQXpDQyw4Q0FBcUM7Ozs7O0lBSXJDLGlEQUErQjs7Ozs7SUFJL0IsNENBQXNEOzs7OztJQUl0RCxvQ0FBOEM7Ozs7O0lBSTlDLDRDQUFpRDs7Ozs7SUFJakQsMkNBQXlCOzs7OztJQUl6Qix5Q0FBdUI7Ozs7O0lBSXZCLHlDQUEyQjs7Ozs7SUFJM0IsOENBQXFDOzs7OztJQUlyQyw4Q0FBNEI7Ozs7O0lBSTVCLDJDQUF5Qjs7Ozs7O0FBTTNCLGtDQWFDOzs7Ozs7Ozs7O0lBTEMscUNBQXVCOzs7OztJQUl2Qix1Q0FBMkIiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGRlc2NyaWJlcyBhIHN0YXRlIG9iamVjdCBmb3IgdGhlIE9wZW5DViBtb2R1bGVcbiAqL1xuaW1wb3J0IHtQb2ludFNoYXBlfSBmcm9tICcuL1ByaXZhdGVNb2RlbHMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE9wZW5DVlN0YXRlIHtcbiAgcmVhZHk6IGJvb2xlYW47XG4gIGxvYWRpbmc6IGJvb2xlYW47XG4gIGVycm9yOiBib29sZWFuO1xuICBzdGF0ZTogc3RyaW5nO1xufVxuXG4vKipcbiAqIGRlc2NyaWJlcyBhbiBvYmplY3Qgd2l0aCB3aWR0aCBhbmQgaGVpZ2h0IHByb3BlcnRpZXNcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBJbWFnZURpbWVuc2lvbnMge1xuICB3aWR0aDogbnVtYmVyO1xuICBoZWlnaHQ6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBkZXNjcmliZXMgYSBjb25maWd1cmF0aW9uIG9iamVjdCBmb3IgdGhlIGVkaXRvclxuICovXG5leHBvcnQgaW50ZXJmYWNlIERvY1NjYW5uZXJDb25maWcge1xuICAvKipcbiAgICogbWF4IGRpbWVuc2lvbnMgb2Ygb3V0cHV0IGltYWdlLiBpZiBzZXQgdG8gemVybyB3aWxsIG5vdCByZXNpemUgdGhlIGltYWdlXG4gICAqL1xuICBtYXhJbWFnZURpbWVuc2lvbnM/OiBJbWFnZURpbWVuc2lvbnM7XG4gIC8qKlxuICAgKiBiYWNrZ3JvdW5kIGNvbG9yIG9mIHRoZSBtYWluIGVkaXRvciBkaXZcbiAgICovXG4gIGVkaXRvckJhY2tncm91bmRDb2xvcj86IHN0cmluZztcbiAgLyoqXG4gICAqIGNzcyBwcm9wZXJ0aWVzIGZvciB0aGUgbWFpbiBlZGl0b3IgZGl2XG4gICAqL1xuICBlZGl0b3JEaW1lbnNpb25zPzogeyB3aWR0aDogc3RyaW5nOyBoZWlnaHQ6IHN0cmluZzsgfTtcbiAgLyoqXG4gICAqIGNzcyB0aGF0IHdpbGwgYmUgYWRkZWQgdG8gdGhlIG1haW4gZGl2IG9mIHRoZSBlZGl0b3IgY29tcG9uZW50XG4gICAqL1xuICBleHRyYUNzcz86IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIHwgbnVtYmVyIH07XG4gIC8qKlxuICAgKiBtYXRlcmlhbCBkZXNpZ24gdGhlbWUgY29sb3IgbmFtZVxuICAgKi9cbiAgYnV0dG9uVGhlbWVDb2xvcj86ICdwcmltYXJ5JyB8ICd3YXJuJyB8ICdhY2NlbnQnO1xuICAvKipcbiAgICogaWNvbiBmb3IgdGhlIGJ1dHRvbiB0aGF0IGNvbXBsZXRlcyB0aGUgZWRpdGluZyBhbmQgZW1pdHMgdGhlIGVkaXRlZCBpbWFnZVxuICAgKi9cbiAgZXhwb3J0SW1hZ2VJY29uPzogc3RyaW5nO1xuICAvKipcbiAgICogY29sb3Igb2YgdGhlIGNyb3AgdG9vbCAocG9pbnRzIGFuZCBjb25uZWN0aW5nIGxpbmVzKVxuICAgKi9cbiAgY3JvcFRvb2xDb2xvcj86IHN0cmluZztcbiAgLyoqXG4gICAqIHNoYXBlIG9mIHRoZSBjcm9wIHRvb2wgcG9pbnRzXG4gICAqL1xuICBjcm9wVG9vbFNoYXBlPzogUG9pbnRTaGFwZTtcbiAgLyoqXG4gICAqIHdpZHRoIGFuZCBoZWlnaHQgb2YgdGhlIGNyb3AgdG9vbCBwb2ludHNcbiAgICovXG4gIGNyb3BUb29sRGltZW5zaW9ucz86IEltYWdlRGltZW5zaW9ucztcbiAgLyoqXG4gICAqIHdlaWdodCBvZiB0aGUgY3JvcCB0b29sJ3MgY29ubmVjdGluZyBsaW5lc1xuICAgKi9cbiAgY3JvcFRvb2xMaW5lV2VpZ2h0PzogbnVtYmVyO1xuICAvKipcbiAgICogbWF4IHdpZHRoIG9mIHRoZSBwcmV2aWV3IHBhbmVcbiAgICovXG4gIG1heFByZXZpZXdXaWR0aD86IG51bWJlcjtcbn1cblxuLyoqXG4gKiBkZXNjcmliZXMgYSBjb25maWd1cmF0aW9uIG9iamVjdCBmb3IgdGhlIE9wZW5DViBzZXJ2aWNlXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgT3BlbkNWQ29uZmlnIHtcbiAgLyoqXG4gICAqIHBhdGggdG8gdGhlIGRpcmVjdG9yeSBjb250YWluaW5nIHRoZSBPcGVuQ1YgZmlsZXMsIGluIHRoZSBmb3JtIG9mICcvcGF0aC90by88b3BlbmN2IGRpcmVjdG9yeT4nXG4gICAqIGRpcmVjdG9yeSBtdXN0IGNvbnRhaW4gdGhlIHRoZSBmb2xsb3dpbmcgZmlsZXM6XG4gICAqIC0tPE9wZW5DdkRpcj5cbiAgICogLS0tLW9wZW5jdi5qc1xuICAgKiAtLS0tb3BlbmN2X2pzLndhc21cbiAgICovXG4gIG9wZW5DVkRpclBhdGg/OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBhZGRpdGlvbmFsIGNhbGxiYWNrIHRoYXQgd2lsbCBydW4gd2hlbiBPcGVuQ3YgaGFzIGZpbmlzaGVkIGxvYWRpbmcgYW5kIHBhcnNpbmdcbiAgICovXG4gIHJ1bk9uT3BlbkNWSW5pdD86IEZ1bmN0aW9uO1xufVxuIl19 -------------------------------------------------------------------------------- /dist/ngx-document-scanner/esm5/ngx-document-scanner.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: ngx-document-scanner.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /** 7 | * Generated bundle index. Do not edit. 8 | */ 9 | export { NgxDocumentScannerModule, NgxDocScannerComponent } from './public_api'; 10 | export { NgxDraggablePointComponent as ɵa } from './lib/components/draggable-point/ngx-draggable-point.component'; 11 | export { NgxFilterMenuComponent as ɵc } from './lib/components/filter-menu/ngx-filter-menu.component'; 12 | export { NgxShapeOutlineComponent as ɵd } from './lib/components/shape-outline/ngx-shape-outline.component'; 13 | export { LimitsService as ɵb } from './lib/services/limits.service'; 14 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LWRvY3VtZW50LXNjYW5uZXIuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9uZ3gtZG9jdW1lbnQtc2Nhbm5lci8iLCJzb3VyY2VzIjpbIm5neC1kb2N1bWVudC1zY2FubmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBSUEsaUVBQWMsY0FBYyxDQUFDO0FBRTdCLE9BQU8sRUFBQywwQkFBMEIsSUFBSSxFQUFFLEVBQUMsTUFBTSxnRUFBZ0UsQ0FBQztBQUNoSCxPQUFPLEVBQUMsc0JBQXNCLElBQUksRUFBRSxFQUFDLE1BQU0sd0RBQXdELENBQUM7QUFDcEcsT0FBTyxFQUFDLHdCQUF3QixJQUFJLEVBQUUsRUFBQyxNQUFNLDREQUE0RCxDQUFDO0FBQzFHLE9BQU8sRUFBQyxhQUFhLElBQUksRUFBRSxFQUFDLE1BQU0sK0JBQStCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEdlbmVyYXRlZCBidW5kbGUgaW5kZXguIERvIG5vdCBlZGl0LlxuICovXG5cbmV4cG9ydCAqIGZyb20gJy4vcHVibGljX2FwaSc7XG5cbmV4cG9ydCB7Tmd4RHJhZ2dhYmxlUG9pbnRDb21wb25lbnQgYXMgybVhfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL2RyYWdnYWJsZS1wb2ludC9uZ3gtZHJhZ2dhYmxlLXBvaW50LmNvbXBvbmVudCc7XG5leHBvcnQge05neEZpbHRlck1lbnVDb21wb25lbnQgYXMgybVjfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL2ZpbHRlci1tZW51L25neC1maWx0ZXItbWVudS5jb21wb25lbnQnO1xuZXhwb3J0IHtOZ3hTaGFwZU91dGxpbmVDb21wb25lbnQgYXMgybVkfSBmcm9tICcuL2xpYi9jb21wb25lbnRzL3NoYXBlLW91dGxpbmUvbmd4LXNoYXBlLW91dGxpbmUuY29tcG9uZW50JztcbmV4cG9ydCB7TGltaXRzU2VydmljZSBhcyDJtWJ9IGZyb20gJy4vbGliL3NlcnZpY2VzL2xpbWl0cy5zZXJ2aWNlJzsiXX0= -------------------------------------------------------------------------------- /dist/ngx-document-scanner/esm5/public_api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: public_api.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /* 7 | * Public API Surface of ngx-document-scanner 8 | */ 9 | export { NgxDocumentScannerModule } from './lib/ngx-document-scanner.module'; 10 | export { NgxDocScannerComponent } from './lib/components/image-editor/ngx-doc-scanner.component'; 11 | export {} from './lib/PublicModels'; 12 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljX2FwaS5qcyIsInNvdXJjZVJvb3QiOiJuZzovL25neC1kb2N1bWVudC1zY2FubmVyLyIsInNvdXJjZXMiOlsicHVibGljX2FwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7OztBQUlBLHlDQUFjLG1DQUFtQyxDQUFDO0FBQ2xELHVDQUFjLHlEQUF5RCxDQUFDO0FBQ3hFLGVBQWMsb0JBQW9CLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogUHVibGljIEFQSSBTdXJmYWNlIG9mIG5neC1kb2N1bWVudC1zY2FubmVyXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9saWIvbmd4LWRvY3VtZW50LXNjYW5uZXIubW9kdWxlJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2NvbXBvbmVudHMvaW1hZ2UtZWRpdG9yL25neC1kb2Mtc2Nhbm5lci5jb21wb25lbnQnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvUHVibGljTW9kZWxzJztcbiJdfQ== -------------------------------------------------------------------------------- /dist/ngx-document-scanner/lib/PrivateModels.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * describes an editor button 3 | */ 4 | import { RolesArray } from './services/limits.service'; 5 | export interface EditorActionButton { 6 | name: string; 7 | type?: 'button' | 'fab'; 8 | icon: string; 9 | action: Function; 10 | text?: string; 11 | mode?: 'crop' | 'color'; 12 | } 13 | /** 14 | * a string describing the shape of a draggable point 15 | */ 16 | export declare type PointShape = 'rect' | 'circle'; 17 | export interface DraggablePointConfig { 18 | width?: number; 19 | height?: number; 20 | color?: string; 21 | shape?: 'rect' | 'circle'; 22 | limitRoles?: RolesArray; 23 | startPosition?: XYPosition; 24 | } 25 | /** 26 | * describes a position on a 2d pane 27 | */ 28 | export interface XYPosition { 29 | x: number; 30 | y: number; 31 | } 32 | /** 33 | * describes o draggable point config options 34 | */ 35 | export interface PointOptions { 36 | width: number; 37 | height: number; 38 | color: string; 39 | shape: PointShape; 40 | } 41 | export interface LimitException { 42 | exceeds: boolean; 43 | resetCoefficients: { 44 | x: 1 | 0 | -1; 45 | y: 1 | 0 | -1; 46 | }; 47 | resetCoordinates: { 48 | x: number; 49 | y: number; 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/lib/PublicModels.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * describes a state object for the OpenCV module 3 | */ 4 | import { PointShape } from './PrivateModels'; 5 | export interface OpenCVState { 6 | ready: boolean; 7 | loading: boolean; 8 | error: boolean; 9 | state: string; 10 | } 11 | /** 12 | * describes an object with width and height properties 13 | */ 14 | export interface ImageDimensions { 15 | width: number; 16 | height: number; 17 | } 18 | /** 19 | * describes a configuration object for the editor 20 | */ 21 | export interface DocScannerConfig { 22 | /** 23 | * max dimensions of output image. if set to zero will not resize the image 24 | */ 25 | maxImageDimensions?: ImageDimensions; 26 | /** 27 | * background color of the main editor div 28 | */ 29 | editorBackgroundColor?: string; 30 | /** 31 | * css properties for the main editor div 32 | */ 33 | editorDimensions?: { 34 | width: string; 35 | height: string; 36 | }; 37 | /** 38 | * css that will be added to the main div of the editor component 39 | */ 40 | extraCss?: { 41 | [key: string]: string | number; 42 | }; 43 | /** 44 | * material design theme color name 45 | */ 46 | buttonThemeColor?: 'primary' | 'warn' | 'accent'; 47 | /** 48 | * icon for the button that completes the editing and emits the edited image 49 | */ 50 | exportImageIcon?: string; 51 | /** 52 | * color of the crop tool (points and connecting lines) 53 | */ 54 | cropToolColor?: string; 55 | /** 56 | * shape of the crop tool points 57 | */ 58 | cropToolShape?: PointShape; 59 | /** 60 | * width and height of the crop tool points 61 | */ 62 | cropToolDimensions?: ImageDimensions; 63 | /** 64 | * weight of the crop tool's connecting lines 65 | */ 66 | cropToolLineWeight?: number; 67 | /** 68 | * max width of the preview pane 69 | */ 70 | maxPreviewWidth?: number; 71 | } 72 | /** 73 | * describes a configuration object for the OpenCV service 74 | */ 75 | export interface OpenCVConfig { 76 | /** 77 | * path to the directory containing the OpenCV files, in the form of '/path/to/' 78 | * directory must contain the the following files: 79 | * -- 80 | * ----opencv.js 81 | * ----opencv_js.wasm 82 | */ 83 | openCVDirPath?: string; 84 | /** 85 | * additional callback that will run when OpenCv has finished loading and parsing 86 | */ 87 | runOnOpenCVInit?: Function; 88 | } 89 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/lib/components/draggable-point/ngx-draggable-point.component.d.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit } from '@angular/core'; 2 | import { LimitsService } from '../../services/limits.service'; 3 | import { XYPosition } from '../../PrivateModels'; 4 | export declare class NgxDraggablePointComponent implements AfterViewInit { 5 | private limitsService; 6 | width: number; 7 | height: number; 8 | color: string; 9 | shape: 'rect' | 'circle'; 10 | pointOptions: 'rect' | 'circle'; 11 | limitRoles: Array<'left' | 'right' | 'top' | 'bottom'>; 12 | startPosition: XYPosition; 13 | container: HTMLElement; 14 | private _currentPosition; 15 | position: XYPosition; 16 | private _paneDimensions; 17 | resetPosition: XYPosition; 18 | constructor(limitsService: LimitsService); 19 | ngAfterViewInit(): void; 20 | /** 21 | * returns a css style object for the point 22 | */ 23 | pointStyle(): { 24 | width: string; 25 | height: string; 26 | 'background-color': string; 27 | 'border-radius': string | number; 28 | position: string; 29 | }; 30 | /** 31 | * registers a position change on the limits service, and adjusts position if necessary 32 | * @param position - the current position of the point 33 | */ 34 | positionChange(position: XYPosition): void; 35 | /** 36 | * adjusts the position of the point after a limit exception 37 | */ 38 | private adjustPosition; 39 | /** 40 | * called on movement end, checks if last position exceeded the limits ad adjusts 41 | */ 42 | movementEnd(position: XYPosition): void; 43 | /** 44 | * calculates the initial positions of the point by it's roles 45 | * @param dimensions - dimensions of the pane in which the point is located 46 | */ 47 | private getInitialPosition; 48 | /** 49 | * repositions the point after an external reposition event 50 | * @param positions - an array of all points on the pane 51 | */ 52 | private externalReposition; 53 | /** 54 | * returns a new point position if the movement exceeded the pane limit 55 | */ 56 | private enforcePaneLimits; 57 | } 58 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/lib/components/filter-menu/ngx-filter-menu.component.d.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from '@angular/core'; 2 | import { EditorActionButton } from '../../PrivateModels'; 3 | import { MatBottomSheetRef } from '@angular/material/bottom-sheet'; 4 | export declare class NgxFilterMenuComponent { 5 | private bottomSheetRef; 6 | data: any; 7 | filterOptions: Array; 8 | filterSelected: EventEmitter; 9 | selectOption(optionName: any): void; 10 | constructor(bottomSheetRef: MatBottomSheetRef, data: any); 11 | } 12 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/lib/components/shape-outline/ngx-shape-outline.component.d.ts: -------------------------------------------------------------------------------- 1 | import { AfterViewInit } from '@angular/core'; 2 | import { LimitsService } from '../../services/limits.service'; 3 | import { ImageDimensions } from '../../PublicModels'; 4 | export declare class NgxShapeOutlineComponent implements AfterViewInit { 5 | private limitsService; 6 | color: string; 7 | weight: number; 8 | dimensions: ImageDimensions; 9 | canvas: any; 10 | private _points; 11 | private _sortedPoints; 12 | constructor(limitsService: LimitsService); 13 | ngAfterViewInit(): void; 14 | /** 15 | * clears the shape canvas 16 | */ 17 | private clearCanvas; 18 | /** 19 | * sorts the array of points according to their clockwise alignment 20 | */ 21 | private sortPoints; 22 | /** 23 | * draws a line between the points according to their order 24 | */ 25 | private drawShape; 26 | } 27 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/lib/ngx-document-scanner.module.d.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders } from '@angular/core'; 2 | import { OpenCVConfig } from './PublicModels'; 3 | export declare class NgxDocumentScannerModule { 4 | static forRoot(config: OpenCVConfig): ModuleWithProviders; 5 | } 6 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/lib/services/limits.service.d.ts: -------------------------------------------------------------------------------- 1 | import { BehaviorSubject } from 'rxjs'; 2 | import { ImageDimensions } from '../PublicModels'; 3 | import { LimitException, XYPosition } from '../PrivateModels'; 4 | export declare class LimitsService { 5 | private limitDirections; 6 | /** 7 | * stores the crop limits limits 8 | */ 9 | private _limits; 10 | /** 11 | * stores the array of the draggable points displayed on the crop area 12 | */ 13 | private _points; 14 | /** 15 | * stores the pane dimensions 16 | */ 17 | private _paneDimensions; 18 | positions: BehaviorSubject>; 19 | repositionEvent: BehaviorSubject>; 20 | limits: BehaviorSubject; 21 | paneDimensions: BehaviorSubject; 22 | constructor(); 23 | /** 24 | * set privew pane dimensions 25 | */ 26 | setPaneDimensions(dimensions: ImageDimensions): Promise; 27 | /** 28 | * repositions points externally 29 | */ 30 | repositionPoints(positions: any): void; 31 | /** 32 | * updates limits and point positions and calls next on the observables 33 | * @param positionChangeData - position change event data 34 | */ 35 | positionChange(positionChangeData: PointPositionChange): void; 36 | /** 37 | * updates the position of the point 38 | * @param positionChange - position change event data 39 | */ 40 | updatePosition(positionChange: PointPositionChange): void; 41 | /** 42 | * check if a position change event exceeds the limits 43 | * @param positionChange - position change event data 44 | * @returns LimitException0 45 | */ 46 | exceedsLimit(positionChange: PointPositionChange): LimitException; 47 | /** 48 | * rotate crop tool points clockwise 49 | * @param resizeRatios - ratio between the new dimensions and the previous 50 | * @param initialPreviewDimensions - preview pane dimensions before rotation 51 | * @param initialPositions - current positions before rotation 52 | */ 53 | rotateClockwise(resizeRatios: any, initialPreviewDimensions: any, initialPositions: Array): void; 54 | /** 55 | * returns the corner positions after a 90 degrees clockwise rotation 56 | */ 57 | private rotateCornerClockwise; 58 | /** 59 | * checks if two array contain the same values 60 | * @param array1 - array 1 61 | * @param array2 - array 2 62 | * @returns boolean 63 | */ 64 | compareArray(array1: Array, array2: Array): boolean; 65 | private getDirectionAxis; 66 | } 67 | export interface PointPositionChange { 68 | x: number; 69 | y: number; 70 | roles: RolesArray; 71 | } 72 | export interface AreaLimits { 73 | top: number; 74 | bottom: number; 75 | right: number; 76 | left: number; 77 | } 78 | export declare type RolesArray = Array; 79 | export declare class PositionChangeData implements PointPositionChange { 80 | x: number; 81 | y: number; 82 | roles: RolesArray; 83 | constructor(position: XYPosition, roles: RolesArray); 84 | } 85 | export declare type Direction = 'left' | 'right' | 'top' | 'bottom'; 86 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/ngx-document-scanner.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated bundle index. Do not edit. 3 | */ 4 | export * from './public_api'; 5 | export { NgxDraggablePointComponent as ɵa } from './lib/components/draggable-point/ngx-draggable-point.component'; 6 | export { NgxFilterMenuComponent as ɵc } from './lib/components/filter-menu/ngx-filter-menu.component'; 7 | export { NgxShapeOutlineComponent as ɵd } from './lib/components/shape-outline/ngx-shape-outline.component'; 8 | export { LimitsService as ɵb } from './lib/services/limits.service'; 9 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-document-scanner", 3 | "version": "1.0.9", 4 | "keywords": [ 5 | "opencv", 6 | "camscanner", 7 | "scanner", 8 | "image processing", 9 | "document", 10 | "angular", 11 | "OpenCVJS", 12 | "image manipulation", 13 | "crop" 14 | ], 15 | "description": "Angular 2+ component for cropping and enhancing images of documents", 16 | "author": "RoiP", 17 | "license": "MIT", 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/roiperlman/ngx-document-scanner.git" 21 | }, 22 | "peerDependencies": { 23 | "@angular/common": "^9.1.9", 24 | "@angular/core": "^9.1.9", 25 | "@angular/platform-browser": "~9.1.9", 26 | "@angular/platform-browser-dynamic": "~9.1.9", 27 | "rxjs": "~6.5.5" 28 | }, 29 | "dependencies": { 30 | "angular2-draggable": "^2.3.2", 31 | "ngx-opencv": "2.0.1", 32 | "@angular/flex-layout": "^9.0.0-beta.31", 33 | "@angular/material": "^9.2.4", 34 | "@angular/cdk": "~9.2.4", 35 | "tslib": "^1.10.0" 36 | }, 37 | "main": "bundles/ngx-document-scanner.umd.js", 38 | "module": "fesm5/ngx-document-scanner.js", 39 | "es2015": "fesm2015/ngx-document-scanner.js", 40 | "esm5": "esm5/ngx-document-scanner.js", 41 | "esm2015": "esm2015/ngx-document-scanner.js", 42 | "fesm5": "fesm5/ngx-document-scanner.js", 43 | "fesm2015": "fesm2015/ngx-document-scanner.js", 44 | "typings": "ngx-document-scanner.d.ts", 45 | "metadata": "ngx-document-scanner.metadata.json", 46 | "sideEffects": false 47 | } 48 | -------------------------------------------------------------------------------- /dist/ngx-document-scanner/public_api.d.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/ngx-document-scanner.module'; 2 | export * from './lib/components/image-editor/ngx-doc-scanner.component'; 3 | export * from './lib/PublicModels'; 4 | -------------------------------------------------------------------------------- /dist/ngx-ds-demo-app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgxDsDemoApp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /dist/ngx-ds-demo-app/assets/git.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/dist/ngx-ds-demo-app/assets/git.jpg -------------------------------------------------------------------------------- /dist/ngx-ds-demo-app/assets/npm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/dist/ngx-ds-demo-app/assets/npm.jpg -------------------------------------------------------------------------------- /dist/ngx-ds-demo-app/assets/opencv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/dist/ngx-ds-demo-app/assets/opencv.png -------------------------------------------------------------------------------- /dist/ngx-ds-demo-app/assets/opencv/opencv_js.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/dist/ngx-ds-demo-app/assets/opencv/opencv_js.wasm -------------------------------------------------------------------------------- /dist/ngx-ds-demo-app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/dist/ngx-ds-demo-app/favicon.ico -------------------------------------------------------------------------------- /dist/ngx-ds-demo-app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgxDsDemoApp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /dist/ngx-ds-demo-app/runtime.ec2944dd8b20ec099bf3.js: -------------------------------------------------------------------------------- 1 | !function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c { 50 | // do something with the state string 51 | this.cvState = cvState.state; 52 | if (cvState.error) { 53 | // handle errors 54 | } else if (cvState.loading) { 55 | // e.g. show loading indicator 56 | } else if (cvState.ready) { 57 | // do image processing stuff 58 | } 59 | }); 60 | } 61 | 62 | **Note that loading and parsing of the OpenCV library is done synchronously, and might take some time while blocking execution of other processes, depending on client's device.** Therefore it's recommended to bind a loading indicator to the state observable. 63 | 64 | The observable emits an `OpenCVState` object when changes occur, with the following properties: 65 | 66 | | property |type | description | 67 | |--|--|--| 68 | | loading | boolean | true when loading of the opencv lib. is initiated, until it's completion callback is fires or the listener pick up an error | 69 | | error | boolean | true when an error is picked up by the listener | 70 | | ready | boolean | true when loading **and parsing** was copleted | 71 | | state | string | indicates the current state of the module as a string | 72 | 73 | 74 | ## Configuration Options 75 | You can configure the service with the OpenCVConfig object 76 | 77 | import {OpenCvConfig} from 'ngx-document-scanner'; 78 | 79 | | property | type | description | 80 | |--|--|--| 81 | |openCVDirPath| string | path to the directory containing the OpenCV files, in the form of `'/path/to/[opencv directory]'`. directory must contain `opencv.js` & `opencv_js.wasm`.| 82 | |runOnOpenCVInit| Function| additional callback that will run when OpenCv has finished loading and parsing. callback runs in the angular zone in the context of the service.| 83 | 84 | -------------------------------------------------------------------------------- /dist/ngx-opencv/bundles/ngx-opencv.umd.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("@angular/core"),require("rxjs")):"function"==typeof define&&define.amd?define("ngx-opencv",["exports","@angular/core","rxjs"],t):t((e=e||self)["ngx-opencv"]={},e.ng.core,e.rxjs)}(this,(function(e,t,n){"use strict";var o=new t.InjectionToken("OpenCV config object token"),r=function(){function e(e,t){this._ngZone=t,this.cvState=new n.BehaviorSubject({ready:!1,error:!1,loading:!0,state:"loading"}),e||(e={}),this.configModule=this.generateConfigModule(e),this.loadOpenCv()}return e.prototype.loadOpenCv=function(){var e=this;this.cvState.next(this.newState("loading")),window.Module=this.configModule;var t=document.createElement("script");t.setAttribute("async",""),t.setAttribute("type","text/javascript"),t.addEventListener("error",(function(){var t=new Error("Failed to load "+e.configModule.scriptUrl);e.cvState.next(e.newState("error")),e.cvState.error(t)}),{passive:!0}),t.src=this.configModule.scriptUrl;var n=document.getElementsByTagName("script")[0];n?n.parentNode.insertBefore(t,n):document.head.appendChild(t)},e.prototype.newState=function(e){var t={ready:!1,loading:!1,error:!1,state:""};return Object.keys(t).forEach((function(n){"state"!==n&&(n===e?(t[n]=!0,t.state=n):t[n]=!1)})),t},e.prototype.generateConfigModule=function(e){var t=this;return{scriptUrl:e.openCVDirPath?e.openCVDirPath+"/opencv.js":"/assets/opencv/opencv.js",wasmBinaryFile:"opencv_js.wasm",usingWasm:!0,onRuntimeInitialized:function(){t._ngZone.run((function(){console.log("openCV Ready"),t.cvState.next(t.newState("ready")),e.runOnOpenCVInit&&e.runOnOpenCVInit()}))}}},e.decorators=[{type:t.Injectable,args:[{providedIn:"root"}]}],e.ctorParameters=function(){return[{type:void 0,decorators:[{type:t.Inject,args:[o]}]},{type:t.NgZone}]},e.ɵprov=t.ɵɵdefineInjectable({factory:function(){return new e(t.ɵɵinject(o),t.ɵɵinject(t.NgZone))},token:e,providedIn:"root"}),e}();var i=function(){function e(){}return e.forRoot=function(t){return{ngModule:e,providers:[{provide:o,useValue:t}]}},e.decorators=[{type:t.NgModule,args:[{declarations:[],exports:[],providers:[r]}]}],e}();e.NgxOpenCVModule=i,e.NgxOpenCVService=r,e.OpenCvConfigToken=o,Object.defineProperty(e,"__esModule",{value:!0})})); 2 | //# sourceMappingURL=ngx-opencv.umd.min.js.map -------------------------------------------------------------------------------- /dist/ngx-opencv/bundles/ngx-opencv.umd.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["ng://ngx-opencv/lib/ngx-open-cv.service.ts","ng://ngx-opencv/lib/ngx-opencv.module.ts"],"names":["OpenCvConfigToken","InjectionToken","NgxOpenCVService","options","_ngZone","this","cvState","BehaviorSubject","ready","error","loading","state","configModule","generateConfigModule","loadOpenCv","prototype","_this","next","newState","window","script","document","createElement","setAttribute","addEventListener","err","Error","scriptUrl","passive","src","node","getElementsByTagName","parentNode","insertBefore","head","appendChild","change","newStateObj","Object","keys","forEach","key","openCVDirPath","wasmBinaryFile","usingWasm","onRuntimeInitialized","run","console","log","runOnOpenCVInit","Injectable","args","providedIn","Inject","NgZone","NgxOpenCVModule","forRoot","config","ngModule","providers","provide","useValue","NgModule","declarations","exports"],"mappings":"iTAIaA,EAAoB,IAAIC,EAAAA,eAA6B,2CAehE,SAAAC,EAAuCC,EAA+BC,GAAAC,KAAAD,QAAAA,EARtEC,KAAAC,QAAU,IAAIC,EAAAA,gBAA6B,CACzCC,OAAO,EACPC,OAAO,EACPC,SAAS,EACTC,MAAO,YAKFR,IACHA,EAAU,IAEZE,KAAKO,aAAeP,KAAKQ,qBAAqBV,GAC9CE,KAAKS,oBAMPZ,EAAAa,UAAAD,WAAA,WAAA,IAAAE,EAAAX,KACEA,KAAKC,QAAQW,KAAMZ,KAAKa,SAAS,YAEjCC,OAAe,OAAId,KAAKO,iBAGlBQ,EAA6BC,SAASC,cAAc,UAC1DF,EAAOG,aAAa,QAAS,IAC7BH,EAAOG,aAAa,OAAQ,mBAG5BH,EAAOI,iBAAiB,SAAO,eACvBC,EAAM,IAAIC,MAAM,kBAAoBV,EAAKJ,aAAae,WAC5DX,EAAKV,QAAQW,KAAKD,EAAKE,SAAS,UAChCF,EAAKV,QAAQG,MAAMgB,KAClB,CAACG,SAAS,IAGbR,EAAOS,IAAMxB,KAAKO,aAAae,cAEzBG,EAAOT,SAASU,qBAAqB,UAAU,GACjDD,EACFA,EAAKE,WAAWC,aAAab,EAAQU,GAErCT,SAASa,KAAKC,YAAYf,IAQtBlB,EAAAa,UAAAG,SAAR,SAAiBkB,OACTC,EAA2B,CAC/B7B,OAAO,EACPE,SAAS,EACTD,OAAO,EACPE,MAAO,IAYT,OAVA2B,OAAOC,KAAKF,GAAaG,SAAO,SAACC,GACnB,UAARA,IACEA,IAAQL,GACVC,EAAYI,IAAO,EACnBJ,EAAY1B,MAAQ8B,GAEpBJ,EAAYI,IAAO,MAIlBJ,GAODnC,EAAAa,UAAAF,qBAAR,SAA6BV,GAA7B,IAAAa,EAAAX,KACE,MAAO,CACLsB,UAAWxB,EAAQuC,cAAmBvC,EAAQuC,cAAa,aAAe,2BAC1EC,eAAgB,iBAChBC,WAAW,EACXC,qBAAoB,WAClB7B,EAAKZ,QAAQ0C,KAAG,WACdC,QAAQC,IAAI,gBACZhC,EAAKV,QAAQW,KAAKD,EAAKE,SAAS,UAC5Bf,EAAQ8C,iBACV9C,EAAQ8C,4CA1FnBC,EAAAA,WAAUC,KAAA,CAAC,CACVC,WAAY,6EAYCC,EAAAA,OAAMF,KAAA,CAACnD,YAnBsBsD,EAAAA,+JCK5C,SAAAC,KAYA,OANSA,EAAAC,QAAP,SAAeC,GACb,MAAO,CACLC,SAAUH,EACVI,UAAW,CAAC,CAAEC,QAAS5D,EAAmB6D,SAAUJ,0BATzDK,EAAAA,SAAQX,KAAA,CAAC,CACRY,aAAc,GACdC,QAAS,GACTL,UAAW,CAACzD,OASdqD","sourcesContent":["import {Inject, Injectable, InjectionToken, NgZone} from '@angular/core';\nimport {BehaviorSubject} from 'rxjs';\nimport {OpenCVConfig, OpenCVState} from './models';\n\nexport const OpenCvConfigToken = new InjectionToken('OpenCV config object token');\n\n@Injectable({\n providedIn: 'root'\n})\nexport class NgxOpenCVService {\n\n cvState = new BehaviorSubject({\n ready: false,\n error: false,\n loading: true,\n state: 'loading'\n });\n configModule: OpenCvConfigModule;\n\n constructor(@Inject(OpenCvConfigToken) options: OpenCVConfig, private _ngZone: NgZone) {\n if (!options) {\n options = {};\n }\n this.configModule = this.generateConfigModule(options);\n this.loadOpenCv();\n }\n\n /**\n * load the OpenCV script\n */\n loadOpenCv() {\n this.cvState.next( this.newState('loading'));\n // create global module variable\n window['Module'] = this.configModule;\n\n // create script element and set attributes\n const script = document.createElement('script');\n script.setAttribute('async', '');\n script.setAttribute('type', 'text/javascript');\n\n // listen for errors\n script.addEventListener('error', () => {\n const err = new Error('Failed to load ' + this.configModule.scriptUrl);\n this.cvState.next(this.newState('error'));\n this.cvState.error(err);\n }, {passive: true});\n\n // set script url\n script.src = this.configModule.scriptUrl;\n // insert script as first script tag\n const node = document.getElementsByTagName('script')[0];\n if (node) {\n node.parentNode.insertBefore(script, node);\n } else {\n document.head.appendChild(script);\n }\n }\n\n /**\n * generates a new state object\n * @param change - the new state of the module\n */\n private newState(change: 'loading'|'ready'|'error'): OpenCVState {\n const newStateObj: OpenCVState = {\n ready: false,\n loading: false,\n error: false,\n state: ''\n };\n Object.keys(newStateObj).forEach(key => {\n if (key !== 'state') {\n if (key === change) {\n newStateObj[key] = true;\n newStateObj.state = key;\n } else {\n newStateObj[key] = false;\n }\n }\n });\n return newStateObj;\n }\n\n /**\n * generates a config module for the global Module object\n * @param options - configuration options\n */\n private generateConfigModule(options: OpenCVConfig): OpenCvConfigModule {\n return {\n scriptUrl: options.openCVDirPath ? `${options.openCVDirPath}/opencv.js` : `/assets/opencv/opencv.js`,\n wasmBinaryFile: 'opencv_js.wasm',\n usingWasm: true,\n onRuntimeInitialized: () => {\n this._ngZone.run(() => {\n console.log('openCV Ready');\n this.cvState.next(this.newState('ready'));\n if (options.runOnOpenCVInit) {\n options.runOnOpenCVInit();\n }\n });\n }\n };\n }\n}\n\n/**\n * describes the global Module object that is used to initiate OpenCV.js\n */\ninterface OpenCvConfigModule {\n scriptUrl: string;\n wasmBinaryFile: string;\n usingWasm: boolean;\n onRuntimeInitialized: Function;\n}\n\n","import {ModuleWithProviders, NgModule} from '@angular/core';\nimport {OpenCVConfig} from './models';\nimport {NgxOpenCVService, OpenCvConfigToken} from './ngx-open-cv.service';\n\n\n@NgModule({\n declarations: [],\n exports: [],\n providers: [NgxOpenCVService]\n})\nexport class NgxOpenCVModule {\n static forRoot(config: OpenCVConfig): ModuleWithProviders {\n return {\n ngModule: NgxOpenCVModule,\n providers: [{ provide: OpenCvConfigToken, useValue: config }]\n };\n }\n}\n\nconst a = 0;\n"]} -------------------------------------------------------------------------------- /dist/ngx-opencv/esm2015/lib/models.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: lib/models.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /** 7 | * describes a configuration object for the OpenCV service 8 | * @record 9 | */ 10 | export function OpenCVConfig() { } 11 | if (false) { 12 | /** 13 | * path to the directory containing the OpenCV files, in the form of '/path/to/' 14 | * directory must contain the the following files: 15 | * -- 16 | * ----opencv.js 17 | * ----opencv_js.wasm 18 | * @type {?|undefined} 19 | */ 20 | OpenCVConfig.prototype.openCVDirPath; 21 | /** 22 | * additional callback that will run when OpenCv has finished loading and parsing 23 | * @type {?|undefined} 24 | */ 25 | OpenCVConfig.prototype.runOnOpenCVInit; 26 | } 27 | /** 28 | * @record 29 | */ 30 | export function OpenCVState() { } 31 | if (false) { 32 | /** @type {?} */ 33 | OpenCVState.prototype.ready; 34 | /** @type {?} */ 35 | OpenCVState.prototype.loading; 36 | /** @type {?} */ 37 | OpenCVState.prototype.error; 38 | /** @type {?} */ 39 | OpenCVState.prototype.state; 40 | } 41 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWxzLmpzIiwic291cmNlUm9vdCI6Im5nOi8vbmd4LW9wZW5jdi8iLCJzb3VyY2VzIjpbImxpYi9tb2RlbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBR0Esa0NBYUM7Ozs7Ozs7Ozs7SUFMQyxxQ0FBdUI7Ozs7O0lBSXZCLHVDQUEyQjs7Ozs7QUFHN0IsaUNBS0M7OztJQUpDLDRCQUFlOztJQUNmLDhCQUFpQjs7SUFDakIsNEJBQWU7O0lBQ2YsNEJBQWMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGRlc2NyaWJlcyBhIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGZvciB0aGUgT3BlbkNWIHNlcnZpY2VcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBPcGVuQ1ZDb25maWcge1xuICAvKipcbiAgICogcGF0aCB0byB0aGUgZGlyZWN0b3J5IGNvbnRhaW5pbmcgdGhlIE9wZW5DViBmaWxlcywgaW4gdGhlIGZvcm0gb2YgJy9wYXRoL3RvLzxvcGVuY3YgZGlyZWN0b3J5PidcbiAgICogZGlyZWN0b3J5IG11c3QgY29udGFpbiB0aGUgdGhlIGZvbGxvd2luZyBmaWxlczpcbiAgICogLS08T3BlbkN2RGlyPlxuICAgKiAtLS0tb3BlbmN2LmpzXG4gICAqIC0tLS1vcGVuY3ZfanMud2FzbVxuICAgKi9cbiAgb3BlbkNWRGlyUGF0aD86IHN0cmluZztcbiAgLyoqXG4gICAqIGFkZGl0aW9uYWwgY2FsbGJhY2sgdGhhdCB3aWxsIHJ1biB3aGVuIE9wZW5DdiBoYXMgZmluaXNoZWQgbG9hZGluZyBhbmQgcGFyc2luZ1xuICAgKi9cbiAgcnVuT25PcGVuQ1ZJbml0PzogRnVuY3Rpb247XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgT3BlbkNWU3RhdGUge1xuICByZWFkeTogYm9vbGVhbjtcbiAgbG9hZGluZzogYm9vbGVhbjtcbiAgZXJyb3I6IGJvb2xlYW47XG4gIHN0YXRlOiBzdHJpbmc7XG59XG4iXX0= -------------------------------------------------------------------------------- /dist/ngx-opencv/esm2015/lib/ngx-opencv.module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: lib/ngx-opencv.module.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | import { NgModule } from '@angular/core'; 7 | import { NgxOpenCVService, OpenCvConfigToken } from './ngx-open-cv.service'; 8 | export class NgxOpenCVModule { 9 | /** 10 | * @param {?} config 11 | * @return {?} 12 | */ 13 | static forRoot(config) { 14 | return { 15 | ngModule: NgxOpenCVModule, 16 | providers: [{ provide: OpenCvConfigToken, useValue: config }] 17 | }; 18 | } 19 | } 20 | NgxOpenCVModule.decorators = [ 21 | { type: NgModule, args: [{ 22 | declarations: [], 23 | exports: [], 24 | providers: [NgxOpenCVService] 25 | },] } 26 | ]; 27 | /** @type {?} */ 28 | const a = 0; 29 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LW9wZW5jdi5tb2R1bGUuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9uZ3gtb3BlbmN2LyIsInNvdXJjZXMiOlsibGliL25neC1vcGVuY3YubW9kdWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsT0FBTyxFQUFzQixRQUFRLEVBQUMsTUFBTSxlQUFlLENBQUM7QUFFNUQsT0FBTyxFQUFDLGdCQUFnQixFQUFFLGlCQUFpQixFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFRMUUsTUFBTSxPQUFPLGVBQWU7Ozs7O0lBQzFCLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBb0I7UUFDakMsT0FBTztZQUNMLFFBQVEsRUFBRSxlQUFlO1lBQ3pCLFNBQVMsRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsQ0FBQztTQUM5RCxDQUFDO0lBQ0osQ0FBQzs7O1lBWEYsUUFBUSxTQUFDO2dCQUNSLFlBQVksRUFBRSxFQUFFO2dCQUNoQixPQUFPLEVBQUUsRUFBRTtnQkFDWCxTQUFTLEVBQUUsQ0FBQyxnQkFBZ0IsQ0FBQzthQUM5Qjs7O01BVUssQ0FBQyxHQUFHLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge01vZHVsZVdpdGhQcm92aWRlcnMsIE5nTW9kdWxlfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7T3BlbkNWQ29uZmlnfSBmcm9tICcuL21vZGVscyc7XG5pbXBvcnQge05neE9wZW5DVlNlcnZpY2UsIE9wZW5DdkNvbmZpZ1Rva2VufSBmcm9tICcuL25neC1vcGVuLWN2LnNlcnZpY2UnO1xuXG5cbkBOZ01vZHVsZSh7XG4gIGRlY2xhcmF0aW9uczogW10sXG4gIGV4cG9ydHM6IFtdLFxuICBwcm92aWRlcnM6IFtOZ3hPcGVuQ1ZTZXJ2aWNlXVxufSlcbmV4cG9ydCBjbGFzcyBOZ3hPcGVuQ1ZNb2R1bGUge1xuICBzdGF0aWMgZm9yUm9vdChjb25maWc6IE9wZW5DVkNvbmZpZyk6IE1vZHVsZVdpdGhQcm92aWRlcnMge1xuICAgIHJldHVybiB7XG4gICAgICBuZ01vZHVsZTogTmd4T3BlbkNWTW9kdWxlLFxuICAgICAgcHJvdmlkZXJzOiBbeyBwcm92aWRlOiBPcGVuQ3ZDb25maWdUb2tlbiwgdXNlVmFsdWU6IGNvbmZpZyB9XVxuICAgIH07XG4gIH1cbn1cblxuY29uc3QgYSA9IDA7XG4iXX0= -------------------------------------------------------------------------------- /dist/ngx-opencv/esm2015/ngx-opencv.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: ngx-opencv.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /** 7 | * Generated bundle index. Do not edit. 8 | */ 9 | export { NgxOpenCVModule, OpenCvConfigToken, NgxOpenCVService } from './public_api'; 10 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LW9wZW5jdi5qcyIsInNvdXJjZVJvb3QiOiJuZzovL25neC1vcGVuY3YvIiwic291cmNlcyI6WyJuZ3gtb3BlbmN2LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBSUEscUVBQWMsY0FBYyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBHZW5lcmF0ZWQgYnVuZGxlIGluZGV4LiBEbyBub3QgZWRpdC5cbiAqL1xuXG5leHBvcnQgKiBmcm9tICcuL3B1YmxpY19hcGknO1xuIl19 -------------------------------------------------------------------------------- /dist/ngx-opencv/esm2015/public_api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: public_api.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | export { NgxOpenCVModule } from './lib/ngx-opencv.module'; 7 | export {} from './lib/models'; 8 | export { OpenCvConfigToken, NgxOpenCVService } from './lib/ngx-open-cv.service'; 9 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljX2FwaS5qcyIsInNvdXJjZVJvb3QiOiJuZzovL25neC1vcGVuY3YvIiwic291cmNlcyI6WyJwdWJsaWNfYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQ0EsZ0NBQWMseUJBQXlCLENBQUM7QUFDeEMsZUFBYyxjQUFjLENBQUM7QUFDN0Isb0RBQWMsMkJBQTJCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJcbmV4cG9ydCAqIGZyb20gJy4vbGliL25neC1vcGVuY3YubW9kdWxlJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL21vZGVscyc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9uZ3gtb3Blbi1jdi5zZXJ2aWNlJztcbiJdfQ== -------------------------------------------------------------------------------- /dist/ngx-opencv/esm5/lib/models.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: lib/models.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /** 7 | * describes a configuration object for the OpenCV service 8 | * @record 9 | */ 10 | export function OpenCVConfig() { } 11 | if (false) { 12 | /** 13 | * path to the directory containing the OpenCV files, in the form of '/path/to/' 14 | * directory must contain the the following files: 15 | * -- 16 | * ----opencv.js 17 | * ----opencv_js.wasm 18 | * @type {?|undefined} 19 | */ 20 | OpenCVConfig.prototype.openCVDirPath; 21 | /** 22 | * additional callback that will run when OpenCv has finished loading and parsing 23 | * @type {?|undefined} 24 | */ 25 | OpenCVConfig.prototype.runOnOpenCVInit; 26 | } 27 | /** 28 | * @record 29 | */ 30 | export function OpenCVState() { } 31 | if (false) { 32 | /** @type {?} */ 33 | OpenCVState.prototype.ready; 34 | /** @type {?} */ 35 | OpenCVState.prototype.loading; 36 | /** @type {?} */ 37 | OpenCVState.prototype.error; 38 | /** @type {?} */ 39 | OpenCVState.prototype.state; 40 | } 41 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWxzLmpzIiwic291cmNlUm9vdCI6Im5nOi8vbmd4LW9wZW5jdi8iLCJzb3VyY2VzIjpbImxpYi9tb2RlbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBR0Esa0NBYUM7Ozs7Ozs7Ozs7SUFMQyxxQ0FBdUI7Ozs7O0lBSXZCLHVDQUEyQjs7Ozs7QUFHN0IsaUNBS0M7OztJQUpDLDRCQUFlOztJQUNmLDhCQUFpQjs7SUFDakIsNEJBQWU7O0lBQ2YsNEJBQWMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIGRlc2NyaWJlcyBhIGNvbmZpZ3VyYXRpb24gb2JqZWN0IGZvciB0aGUgT3BlbkNWIHNlcnZpY2VcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBPcGVuQ1ZDb25maWcge1xuICAvKipcbiAgICogcGF0aCB0byB0aGUgZGlyZWN0b3J5IGNvbnRhaW5pbmcgdGhlIE9wZW5DViBmaWxlcywgaW4gdGhlIGZvcm0gb2YgJy9wYXRoL3RvLzxvcGVuY3YgZGlyZWN0b3J5PidcbiAgICogZGlyZWN0b3J5IG11c3QgY29udGFpbiB0aGUgdGhlIGZvbGxvd2luZyBmaWxlczpcbiAgICogLS08T3BlbkN2RGlyPlxuICAgKiAtLS0tb3BlbmN2LmpzXG4gICAqIC0tLS1vcGVuY3ZfanMud2FzbVxuICAgKi9cbiAgb3BlbkNWRGlyUGF0aD86IHN0cmluZztcbiAgLyoqXG4gICAqIGFkZGl0aW9uYWwgY2FsbGJhY2sgdGhhdCB3aWxsIHJ1biB3aGVuIE9wZW5DdiBoYXMgZmluaXNoZWQgbG9hZGluZyBhbmQgcGFyc2luZ1xuICAgKi9cbiAgcnVuT25PcGVuQ1ZJbml0PzogRnVuY3Rpb247XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgT3BlbkNWU3RhdGUge1xuICByZWFkeTogYm9vbGVhbjtcbiAgbG9hZGluZzogYm9vbGVhbjtcbiAgZXJyb3I6IGJvb2xlYW47XG4gIHN0YXRlOiBzdHJpbmc7XG59XG4iXX0= -------------------------------------------------------------------------------- /dist/ngx-opencv/esm5/lib/ngx-opencv.module.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: lib/ngx-opencv.module.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | import { NgModule } from '@angular/core'; 7 | import { NgxOpenCVService, OpenCvConfigToken } from './ngx-open-cv.service'; 8 | var NgxOpenCVModule = /** @class */ (function () { 9 | function NgxOpenCVModule() { 10 | } 11 | /** 12 | * @param {?} config 13 | * @return {?} 14 | */ 15 | NgxOpenCVModule.forRoot = /** 16 | * @param {?} config 17 | * @return {?} 18 | */ 19 | function (config) { 20 | return { 21 | ngModule: NgxOpenCVModule, 22 | providers: [{ provide: OpenCvConfigToken, useValue: config }] 23 | }; 24 | }; 25 | NgxOpenCVModule.decorators = [ 26 | { type: NgModule, args: [{ 27 | declarations: [], 28 | exports: [], 29 | providers: [NgxOpenCVService] 30 | },] } 31 | ]; 32 | return NgxOpenCVModule; 33 | }()); 34 | export { NgxOpenCVModule }; 35 | /** @type {?} */ 36 | var a = 0; 37 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LW9wZW5jdi5tb2R1bGUuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9uZ3gtb3BlbmN2LyIsInNvdXJjZXMiOlsibGliL25neC1vcGVuY3YubW9kdWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsT0FBTyxFQUFzQixRQUFRLEVBQUMsTUFBTSxlQUFlLENBQUM7QUFFNUQsT0FBTyxFQUFDLGdCQUFnQixFQUFFLGlCQUFpQixFQUFDLE1BQU0sdUJBQXVCLENBQUM7QUFHMUU7SUFBQTtJQVlBLENBQUM7Ozs7O0lBTlEsdUJBQU87Ozs7SUFBZCxVQUFlLE1BQW9CO1FBQ2pDLE9BQU87WUFDTCxRQUFRLEVBQUUsZUFBZTtZQUN6QixTQUFTLEVBQUUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLENBQUM7U0FDOUQsQ0FBQztJQUNKLENBQUM7O2dCQVhGLFFBQVEsU0FBQztvQkFDUixZQUFZLEVBQUUsRUFBRTtvQkFDaEIsT0FBTyxFQUFFLEVBQUU7b0JBQ1gsU0FBUyxFQUFFLENBQUMsZ0JBQWdCLENBQUM7aUJBQzlCOztJQVFELHNCQUFDO0NBQUEsQUFaRCxJQVlDO1NBUFksZUFBZTs7SUFTdEIsQ0FBQyxHQUFHLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge01vZHVsZVdpdGhQcm92aWRlcnMsIE5nTW9kdWxlfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7T3BlbkNWQ29uZmlnfSBmcm9tICcuL21vZGVscyc7XG5pbXBvcnQge05neE9wZW5DVlNlcnZpY2UsIE9wZW5DdkNvbmZpZ1Rva2VufSBmcm9tICcuL25neC1vcGVuLWN2LnNlcnZpY2UnO1xuXG5cbkBOZ01vZHVsZSh7XG4gIGRlY2xhcmF0aW9uczogW10sXG4gIGV4cG9ydHM6IFtdLFxuICBwcm92aWRlcnM6IFtOZ3hPcGVuQ1ZTZXJ2aWNlXVxufSlcbmV4cG9ydCBjbGFzcyBOZ3hPcGVuQ1ZNb2R1bGUge1xuICBzdGF0aWMgZm9yUm9vdChjb25maWc6IE9wZW5DVkNvbmZpZyk6IE1vZHVsZVdpdGhQcm92aWRlcnMge1xuICAgIHJldHVybiB7XG4gICAgICBuZ01vZHVsZTogTmd4T3BlbkNWTW9kdWxlLFxuICAgICAgcHJvdmlkZXJzOiBbeyBwcm92aWRlOiBPcGVuQ3ZDb25maWdUb2tlbiwgdXNlVmFsdWU6IGNvbmZpZyB9XVxuICAgIH07XG4gIH1cbn1cblxuY29uc3QgYSA9IDA7XG4iXX0= -------------------------------------------------------------------------------- /dist/ngx-opencv/esm5/ngx-opencv.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: ngx-opencv.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | /** 7 | * Generated bundle index. Do not edit. 8 | */ 9 | export { NgxOpenCVModule, OpenCvConfigToken, NgxOpenCVService } from './public_api'; 10 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmd4LW9wZW5jdi5qcyIsInNvdXJjZVJvb3QiOiJuZzovL25neC1vcGVuY3YvIiwic291cmNlcyI6WyJuZ3gtb3BlbmN2LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBSUEscUVBQWMsY0FBYyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBHZW5lcmF0ZWQgYnVuZGxlIGluZGV4LiBEbyBub3QgZWRpdC5cbiAqL1xuXG5leHBvcnQgKiBmcm9tICcuL3B1YmxpY19hcGknO1xuIl19 -------------------------------------------------------------------------------- /dist/ngx-opencv/esm5/public_api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview added by tsickle 3 | * Generated from: public_api.ts 4 | * @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc 5 | */ 6 | export { NgxOpenCVModule } from './lib/ngx-opencv.module'; 7 | export {} from './lib/models'; 8 | export { OpenCvConfigToken, NgxOpenCVService } from './lib/ngx-open-cv.service'; 9 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljX2FwaS5qcyIsInNvdXJjZVJvb3QiOiJuZzovL25neC1vcGVuY3YvIiwic291cmNlcyI6WyJwdWJsaWNfYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQ0EsZ0NBQWMseUJBQXlCLENBQUM7QUFDeEMsZUFBYyxjQUFjLENBQUM7QUFDN0Isb0RBQWMsMkJBQTJCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJcbmV4cG9ydCAqIGZyb20gJy4vbGliL25neC1vcGVuY3YubW9kdWxlJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL21vZGVscyc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9uZ3gtb3Blbi1jdi5zZXJ2aWNlJztcbiJdfQ== -------------------------------------------------------------------------------- /dist/ngx-opencv/fesm2015/ngx-opencv.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"ngx-opencv.js","sources":["ng://ngx-opencv/lib/ngx-open-cv.service.ts","ng://ngx-opencv/lib/ngx-opencv.module.ts","ng://ngx-opencv/lib/models.ts"],"sourcesContent":["import {Inject, Injectable, InjectionToken, NgZone} from '@angular/core';\nimport {BehaviorSubject} from 'rxjs';\nimport {OpenCVConfig, OpenCVState} from './models';\n\nexport const OpenCvConfigToken = new InjectionToken('OpenCV config object token');\n\n@Injectable({\n providedIn: 'root'\n})\nexport class NgxOpenCVService {\n\n cvState = new BehaviorSubject({\n ready: false,\n error: false,\n loading: true,\n state: 'loading'\n });\n configModule: OpenCvConfigModule;\n\n constructor(@Inject(OpenCvConfigToken) options: OpenCVConfig, private _ngZone: NgZone) {\n if (!options) {\n options = {};\n }\n this.configModule = this.generateConfigModule(options);\n this.loadOpenCv();\n }\n\n /**\n * load the OpenCV script\n */\n loadOpenCv() {\n this.cvState.next( this.newState('loading'));\n // create global module variable\n window['Module'] = this.configModule;\n\n // create script element and set attributes\n const script = document.createElement('script');\n script.setAttribute('async', '');\n script.setAttribute('type', 'text/javascript');\n\n // listen for errors\n script.addEventListener('error', () => {\n const err = new Error('Failed to load ' + this.configModule.scriptUrl);\n this.cvState.next(this.newState('error'));\n this.cvState.error(err);\n }, {passive: true});\n\n // set script url\n script.src = this.configModule.scriptUrl;\n // insert script as first script tag\n const node = document.getElementsByTagName('script')[0];\n if (node) {\n node.parentNode.insertBefore(script, node);\n } else {\n document.head.appendChild(script);\n }\n }\n\n /**\n * generates a new state object\n * @param change - the new state of the module\n */\n private newState(change: 'loading'|'ready'|'error'): OpenCVState {\n const newStateObj: OpenCVState = {\n ready: false,\n loading: false,\n error: false,\n state: ''\n };\n Object.keys(newStateObj).forEach(key => {\n if (key !== 'state') {\n if (key === change) {\n newStateObj[key] = true;\n newStateObj.state = key;\n } else {\n newStateObj[key] = false;\n }\n }\n });\n return newStateObj;\n }\n\n /**\n * generates a config module for the global Module object\n * @param options - configuration options\n */\n private generateConfigModule(options: OpenCVConfig): OpenCvConfigModule {\n return {\n scriptUrl: options.openCVDirPath ? `${options.openCVDirPath}/opencv.js` : `/assets/opencv/opencv.js`,\n wasmBinaryFile: 'opencv_js.wasm',\n usingWasm: true,\n onRuntimeInitialized: () => {\n this._ngZone.run(() => {\n console.log('openCV Ready');\n this.cvState.next(this.newState('ready'));\n if (options.runOnOpenCVInit) {\n options.runOnOpenCVInit();\n }\n });\n }\n };\n }\n}\n\n/**\n * describes the global Module object that is used to initiate OpenCV.js\n */\ninterface OpenCvConfigModule {\n scriptUrl: string;\n wasmBinaryFile: string;\n usingWasm: boolean;\n onRuntimeInitialized: Function;\n}\n\n","import {ModuleWithProviders, NgModule} from '@angular/core';\nimport {OpenCVConfig} from './models';\nimport {NgxOpenCVService, OpenCvConfigToken} from './ngx-open-cv.service';\n\n\n@NgModule({\n declarations: [],\n exports: [],\n providers: [NgxOpenCVService]\n})\nexport class NgxOpenCVModule {\n static forRoot(config: OpenCVConfig): ModuleWithProviders {\n return {\n ngModule: NgxOpenCVModule,\n providers: [{ provide: OpenCvConfigToken, useValue: config }]\n };\n }\n}\n\nconst a = 0;\n","/**\n * describes a configuration object for the OpenCV service\n */\nexport interface OpenCVConfig {\n /**\n * path to the directory containing the OpenCV files, in the form of '/path/to/'\n * directory must contain the the following files:\n * --\n * ----opencv.js\n * ----opencv_js.wasm\n */\n openCVDirPath?: string;\n /**\n * additional callback that will run when OpenCv has finished loading and parsing\n */\n runOnOpenCVInit?: Function;\n}\n\nexport interface OpenCVState {\n ready: boolean;\n loading: boolean;\n error: boolean;\n state: string;\n}\n"],"names":[],"mappings":";;;;;;;;;MAIa,iBAAiB,GAAG,IAAI,cAAc,CAAe,4BAA4B;MAKjF,gBAAgB;;;;;IAU3B,YAAuC,OAAqB,EAAU,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;QARrF,YAAO,GAAG,IAAI,eAAe,CAAc;YACzC,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QAID,IAAI,CAAC,OAAO,EAAE;YACZ,OAAO,GAAG,EAAE,CAAC;SACd;QACD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,UAAU,EAAE,CAAC;KACnB;;;;;IAKD,UAAU;QACR,IAAI,CAAC,OAAO,CAAC,IAAI,CAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;;QAE7C,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC;;;cAG/B,MAAM,sBAAuB,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAA;QACnE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;;QAG/C,MAAM,CAAC,gBAAgB,CAAC,OAAO;;;QAAE;;kBACzB,GAAG,GAAG,IAAI,KAAK,CAAC,iBAAiB,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC;YACtE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;YAC1C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;SACzB,GAAE,EAAC,OAAO,EAAE,IAAI,EAAC,CAAC,CAAC;;QAGpB,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC;;;cAEnC,IAAI,GAAG,QAAQ,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACvD,IAAI,IAAI,EAAE;YACR,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;SAC5C;aAAM;YACL,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;SACnC;KACF;;;;;;;IAMO,QAAQ,CAAC,MAAiC;;cAC1C,WAAW,GAAgB;YAC/B,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,EAAE;SACV;QACD,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO;;;;QAAC,GAAG;YAClC,IAAI,GAAG,KAAK,OAAO,EAAE;gBACnB,IAAI,GAAG,KAAK,MAAM,EAAE;oBAClB,WAAW,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;oBACxB,WAAW,CAAC,KAAK,GAAG,GAAG,CAAC;iBACzB;qBAAM;oBACL,WAAW,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;iBAC1B;aACF;SACF,EAAC,CAAC;QACH,OAAO,WAAW,CAAC;KACpB;;;;;;;IAMO,oBAAoB,CAAC,OAAqB;QAChD,OAAO;YACL,SAAS,EAAE,OAAO,CAAC,aAAa,GAAG,GAAG,OAAO,CAAC,aAAa,YAAY,GAAG,0BAA0B;YACpG,cAAc,EAAE,gBAAgB;YAChC,SAAS,EAAE,IAAI;YACf,oBAAoB;;;YAAE;gBACpB,IAAI,CAAC,OAAO,CAAC,GAAG;;;gBAAC;oBACf,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;oBAC5B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;oBAC1C,IAAI,OAAO,CAAC,eAAe,EAAE;wBAC3B,OAAO,CAAC,eAAe,EAAE,CAAC;qBAC3B;iBACF,EAAC,CAAC;aACJ,CAAA;SACF,CAAC;KACH;;;YA/FF,UAAU,SAAC;gBACV,UAAU,EAAE,MAAM;aACnB;;;;4CAWc,MAAM,SAAC,iBAAiB;YAnBK,MAAM;;;;;IAWhD,mCAKG;;IACH,wCAAiC;;;;;IAE6B,mCAAuB;;;;;;AAwFvF,iCAKC;;;IAJC,uCAAkB;;IAClB,4CAAuB;;IACvB,uCAAmB;;IACnB,kDAA+B;;;;;;;;MCrGpB,eAAe;;;;;IAC1B,OAAO,OAAO,CAAC,MAAoB;QACjC,OAAO;YACL,QAAQ,EAAE,eAAe;YACzB,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;SAC9D,CAAC;KACH;;;YAXF,QAAQ,SAAC;gBACR,YAAY,EAAE,EAAE;gBAChB,OAAO,EAAE,EAAE;gBACX,SAAS,EAAE,CAAC,gBAAgB,CAAC;aAC9B;;;MAUK,CAAC,GAAG,CAAC;;;;;;;;;;;2BCHV;;;;;;;;;;IALC,qCAAuB;;;;;IAIvB,uCAA2B;;;;;0BAQ5B;;;IAJC,4BAAe;;IACf,8BAAiB;;IACjB,4BAAe;;IACf,4BAAc;;;;;;;;;;;;;;;;;"} -------------------------------------------------------------------------------- /dist/ngx-opencv/lib/models.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * describes a configuration object for the OpenCV service 3 | */ 4 | export interface OpenCVConfig { 5 | /** 6 | * path to the directory containing the OpenCV files, in the form of '/path/to/' 7 | * directory must contain the the following files: 8 | * -- 9 | * ----opencv.js 10 | * ----opencv_js.wasm 11 | */ 12 | openCVDirPath?: string; 13 | /** 14 | * additional callback that will run when OpenCv has finished loading and parsing 15 | */ 16 | runOnOpenCVInit?: Function; 17 | } 18 | export interface OpenCVState { 19 | ready: boolean; 20 | loading: boolean; 21 | error: boolean; 22 | state: string; 23 | } 24 | -------------------------------------------------------------------------------- /dist/ngx-opencv/lib/ngx-open-cv.service.d.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken, NgZone } from '@angular/core'; 2 | import { BehaviorSubject } from 'rxjs'; 3 | import { OpenCVConfig, OpenCVState } from './models'; 4 | export declare const OpenCvConfigToken: InjectionToken; 5 | export declare class NgxOpenCVService { 6 | private _ngZone; 7 | cvState: BehaviorSubject; 8 | configModule: OpenCvConfigModule; 9 | constructor(options: OpenCVConfig, _ngZone: NgZone); 10 | /** 11 | * load the OpenCV script 12 | */ 13 | loadOpenCv(): void; 14 | /** 15 | * generates a new state object 16 | * @param change - the new state of the module 17 | */ 18 | private newState; 19 | /** 20 | * generates a config module for the global Module object 21 | * @param options - configuration options 22 | */ 23 | private generateConfigModule; 24 | } 25 | /** 26 | * describes the global Module object that is used to initiate OpenCV.js 27 | */ 28 | interface OpenCvConfigModule { 29 | scriptUrl: string; 30 | wasmBinaryFile: string; 31 | usingWasm: boolean; 32 | onRuntimeInitialized: Function; 33 | } 34 | export {}; 35 | -------------------------------------------------------------------------------- /dist/ngx-opencv/lib/ngx-opencv.module.d.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders } from '@angular/core'; 2 | import { OpenCVConfig } from './models'; 3 | export declare class NgxOpenCVModule { 4 | static forRoot(config: OpenCVConfig): ModuleWithProviders; 5 | } 6 | -------------------------------------------------------------------------------- /dist/ngx-opencv/ngx-opencv.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated bundle index. Do not edit. 3 | */ 4 | export * from './public_api'; 5 | -------------------------------------------------------------------------------- /dist/ngx-opencv/ngx-opencv.metadata.json: -------------------------------------------------------------------------------- 1 | {"__symbolic":"module","version":4,"metadata":{"NgxOpenCVModule":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"NgModule","line":5,"character":1},"arguments":[{"declarations":[],"exports":[],"providers":[{"__symbolic":"reference","name":"NgxOpenCVService"}]}]}],"members":{},"statics":{"forRoot":{"__symbolic":"function","parameters":["config"],"value":{"ngModule":{"__symbolic":"reference","name":"NgxOpenCVModule"},"providers":[{"provide":{"__symbolic":"reference","name":"OpenCvConfigToken"},"useValue":{"__symbolic":"reference","name":"config"}}]}}}},"OpenCVConfig":{"__symbolic":"interface"},"OpenCVState":{"__symbolic":"interface"},"OpenCvConfigToken":{"__symbolic":"new","expression":{"__symbolic":"reference","module":"@angular/core","name":"InjectionToken","line":4,"character":37},"arguments":["OpenCV config object token"]},"NgxOpenCVService":{"__symbolic":"class","decorators":[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Injectable","line":6,"character":1},"arguments":[{"providedIn":"root"}]}],"members":{"__ctor__":[{"__symbolic":"constructor","parameterDecorators":[[{"__symbolic":"call","expression":{"__symbolic":"reference","module":"@angular/core","name":"Inject","line":19,"character":15},"arguments":[{"__symbolic":"reference","name":"OpenCvConfigToken"}]}],null],"parameters":[{"__symbolic":"reference","name":"OpenCVConfig"},{"__symbolic":"reference","module":"@angular/core","name":"NgZone","line":19,"character":81}]}],"loadOpenCv":[{"__symbolic":"method"}],"newState":[{"__symbolic":"method"}],"generateConfigModule":[{"__symbolic":"method"}]},"statics":{"ɵprov":{}}}},"origins":{"NgxOpenCVModule":"./lib/ngx-opencv.module","OpenCVConfig":"./lib/models","OpenCVState":"./lib/models","OpenCvConfigToken":"./lib/ngx-open-cv.service","NgxOpenCVService":"./lib/ngx-open-cv.service"},"importAs":"ngx-opencv"} -------------------------------------------------------------------------------- /dist/ngx-opencv/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-opencv", 3 | "version": "1.0.5", 4 | "peerDependencies": { 5 | "@angular/common": "^9.1.9", 6 | "@angular/core": "^9.1.9", 7 | "ngx-opencv": "^2.0.1" 8 | }, 9 | "keywords": [ 10 | "opencv", 11 | "camscanner", 12 | "scanner", 13 | "image processing", 14 | "document", 15 | "angular", 16 | "OpenCVJS", 17 | "image manipulation", 18 | "crop" 19 | ], 20 | "description": "Angular service for implementing the OpenCV library in angular based applications", 21 | "author": "RoiP", 22 | "license": "MIT", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/roiperlman/ngx-document-scanner.git" 26 | }, 27 | "main": "bundles/ngx-opencv.umd.js", 28 | "module": "fesm5/ngx-opencv.js", 29 | "es2015": "fesm2015/ngx-opencv.js", 30 | "esm5": "esm5/ngx-opencv.js", 31 | "esm2015": "esm2015/ngx-opencv.js", 32 | "fesm5": "fesm5/ngx-opencv.js", 33 | "fesm2015": "fesm2015/ngx-opencv.js", 34 | "typings": "ngx-opencv.d.ts", 35 | "metadata": "ngx-opencv.metadata.json", 36 | "sideEffects": false, 37 | "dependencies": { 38 | "tslib": "^1.10.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /dist/ngx-opencv/public_api.d.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/ngx-opencv.module'; 2 | export * from './lib/models'; 3 | export * from './lib/ngx-open-cv.service'; 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-document-scanner", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "build_lib_doc_scanner": "ng build ngx-document-scanner", 7 | "build_lib_opencv": "ng build ngx-opencv", 8 | "build_demo_app": "ng build ngx-ds-demo-app --prod --base-href /ngx-document-scanner/ && npx ngh --dir=dist/ngx-ds-demo-app --no-silent --repo=https://github.com/roiperlman/ngx-document-scanner.git", 9 | "serve_demo_app": "ng serve ngx-ds-demo-app --port 4300", 10 | "publish": "npx ngh --dir=dist/ngx-ds-demo-app --no-silent --repo=https://github.com/roiperlman/ngx-document-scanner.git" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~9.1.9", 15 | "@angular/cdk": "~9.2.4", 16 | "@angular/common": "~9.1.9", 17 | "@angular/compiler": "~9.1.9", 18 | "@angular/core": "~9.1.9", 19 | "@angular/flex-layout": "^9.0.0-beta.31", 20 | "@angular/forms": "~9.1.9", 21 | "@angular/http": "~7.2.16", 22 | "@angular/material": "^9.2.4", 23 | "@angular/platform-browser": "~9.1.9", 24 | "@angular/platform-browser-dynamic": "~9.1.9", 25 | "@angular/router": "~9.1.9", 26 | "angular2-draggable": "^2.3.2", 27 | "core-js": "^3.6.5", 28 | "hammerjs": "^2.0.8", 29 | "ng-open-cv": "^0.3.1", 30 | "rxjs": "~6.5.5", 31 | "typedoc": "^0.17.7", 32 | "webpack-dev-server": "^3.11.0", 33 | "zone.js": "~0.10.3" 34 | }, 35 | "devDependencies": { 36 | "@angular-devkit/build-angular": "~0.901.7", 37 | "@angular-devkit/build-ng-packagr": "~0.901.7", 38 | "@angular/cli": "~9.1.7", 39 | "@angular/compiler-cli": "~9.1.9", 40 | "@angular/language-service": "~9.1.9", 41 | "@types/jasmine": "~3.5.10", 42 | "@types/jasminewd2": "~2.0.8", 43 | "@types/node": "~14.0.11", 44 | "angular-cli-ghpages": "^0.6.2", 45 | "codelyzer": "~5.2.2", 46 | "jasmine-core": "~3.5.0", 47 | "jasmine-spec-reporter": "~5.0.2", 48 | "karma": "~5.0.9", 49 | "karma-chrome-launcher": "~3.1.0", 50 | "karma-coverage-istanbul-reporter": "~3.0.3", 51 | "karma-jasmine": "~3.3.1", 52 | "karma-jasmine-html-reporter": "^1.5.4", 53 | "ng-packagr": "^9.1.5", 54 | "protractor": "~7.0.0", 55 | "ts-node": "~8.10.2", 56 | "tsickle": ">=0.38.1", 57 | "tslib": "^2.0.0", 58 | "tslint": "~6.1.2", 59 | "typedoc-plugin-markdown": "^2.3.1", 60 | "typescript": "~3.8.3" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/README.md: -------------------------------------------------------------------------------- 1 | # Ngx Document Scanner 2 | 3 | An Angular component for cropping and enhancing images of documents, for implementation on a mobile or desktop app. 4 | It uses a [WASM](https://webassembly.org/) build of [OpenCV](https://opencv.org/) to manipulate images, to achieve near-native performance. 5 | Note that there are a few extra steps required to configure the component other than installing the package from npm. 6 | 7 | ## Live Demo 8 | View a live demo **[here](https://roiperlman.github.io/ngx-document-scanner)** 9 | 10 | ## Installation & Setup 11 | install the package via npm 12 | 13 | npm install ngx-document-scanner --save 14 | 15 | the UI is based on `@angular/material`, if you don't have it installed: 16 | 17 | ng add @angular/material 18 | 19 | choose 'yes' when prompted if you wish to add angular animations as it is needed for some of the components. 20 | 21 | ##### Configure OpenCV 22 | copy the opencv.js files to your assets folder (or any other folder). you can build the files yourself ([instructions on the OpenCV site](https://docs.opencv.org/3.4/d4/da1/tutorial_js_setup.html)), or download them from this package's [repository](https://github.com/roiperlman/ngx-document-scanner). 23 | both opencv.js & opencv_js.wasm need to placed in the same folder. 24 | 25 | import the package to your `app.module`. you'll need to configure the location of the open cv files. 26 | 27 | import {OpenCVConfig} from 'ngx-document-scanner'; 28 | import {NgxDocumentScannerModule} from 'ngx-document-scanner'; 29 | 30 | // set the location of the OpenCV files 31 | const openCVConfig: OpenCVConfig = { 32 | openCVDirPath: '/assets/opencv' 33 | }; 34 | 35 | @NgModule({ imports: 36 | [NgxDocumentScannerModule.forRoot(openCVConfig)], 37 | bootstrap: [AppComponent] 38 | }) 39 | export class AppModule { } 40 | 41 | ## Usage 42 | 43 | #### add component to template and bind to inputs and outputs. 44 | 45 | 49 | (editResult)="editResult($event)" 50 | (exitEditor)="exitEditor($event)" 51 | (error)="onError($event)" 52 | (processing)="editorState($event)" 53 | 54 | 55 | #### set configuration options. for example: 56 | 57 | config: DocScannerConfig = { 58 | editorBackgroundColor: '#fafafa', 59 | buttonThemeColor: 'primary', 60 | cropToolColor: '#ff4081', 61 | cropToolShape: 'circle', 62 | exportImageIcon: 'cloud_download' 63 | }; 64 | 65 | ## Component I\O 66 | ### Inputs 67 | 68 | |input|type|description| 69 | |--|--|--| 70 | | **file** | `File` | sets an image for editing | 71 | | **config** | `DocScannerConfig` | configuration object for the component. see [section](#config) dedicated to te config object. | 72 | 73 | ### Outputs 74 | 75 | |output|type|description| 76 | |--|--|--| 77 | | **error** | `EventEmitter` | fires on error | 78 | | **editResult** | `EventEmitter` | fires when the users submits the image | 79 | |**exitEditor**| `EventEmitter`| fires when the user exits the editor| 80 | |**processing**|`EventEmitter`|fires true when the editor is prcessing or loading\parsing the OpenCV module. 81 | 82 | 83 | 84 | ## Configuration Object 85 | optional configuration values that can be passed to the component. 86 | 87 | import {DocScannerConfig} form 'ngx-document-scanner' 88 | config: DocScannerConfig = { 89 | .... 90 | } 91 | 92 | | property | type | description | 93 | |--|--|--| 94 | |**buttonThemeColor** | "primary" | "warn" | "accent" | material design theme color name for the buttons on the component| 95 | |**cropToolColor**|`string`|color of the crop tool (points and connecting lines) | 96 | |**cropToolDimensions** | `{width: number; height: nubmer;}`| width and height of the crop tool points| 97 | |**cropToolLineWeight** |`number`|weight of the crop tool's connecting lines | 98 | |**cropToolShape**|`'rect' | 'circle'`|shape of the crop tool points | 99 | |**editorBackgroundColor**|`string`|background color of the main editor div | 100 | |**editorDimensions** | an object of css keys value pairs| css properties for the main editor div | 101 | |**exportImageIcon** |`string`| icon for the button that completes the editing and emits the edited image.| 102 | **extraCss**|an object of css keys value pairs|css that will be added to the main div of the editor component | 103 | |**maxImageDimensions** | `{width: number; height: nubmer;}` | max dimensions of oputput image. if set to zero will not resize the image.| 104 | |**maxPreviewWidth** | `number`|max width of the preview pane| 105 | 106 | ## Ngx-OpenCV 107 | The angular service used to load the open cv library and monitor it's state is also available as a standalone package: [NgxOpenCV](https://www.npmjs.com/ngx-opencv) 108 | 109 | ## License 110 | 111 | This project is licensed under the MIT License. 112 | 113 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ngx-document-scanner", 4 | "lib": { 5 | "entryFile": "src/public_api.ts" 6 | }, 7 | "whitelistedNonPeerDependencies": ["."] 8 | } 9 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-document-scanner", 3 | "version": "1.0.9", 4 | "keywords": [ 5 | "opencv", 6 | "camscanner", 7 | "scanner", 8 | "image processing", 9 | "document", 10 | "angular", 11 | "OpenCVJS", 12 | "image manipulation", 13 | "crop" 14 | ], 15 | "description": "Angular 2+ component for cropping and enhancing images of documents", 16 | "author": "RoiP", 17 | "license": "MIT", 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/roiperlman/ngx-document-scanner.git" 21 | }, 22 | "peerDependencies": { 23 | "@angular/common": "^9.1.9", 24 | "@angular/core": "^9.1.9", 25 | "@angular/platform-browser": "~9.1.9", 26 | "@angular/platform-browser-dynamic": "~9.1.9", 27 | "rxjs": "~6.5.5" 28 | }, 29 | "dependencies": { 30 | "angular2-draggable": "^2.3.2", 31 | "ngx-opencv": "2.0.1", 32 | "@angular/flex-layout": "^9.0.0-beta.31", 33 | "@angular/material": "^9.2.4", 34 | "@angular/cdk": "~9.2.4" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/PrivateModels.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * describes an editor button 3 | */ 4 | import {RolesArray} from './services/limits.service'; 5 | 6 | export interface EditorActionButton { 7 | name: string; 8 | type?: 'button' | 'fab'; 9 | icon: string; 10 | action: Function; 11 | text?: string; 12 | mode?: 'crop' | 'color'; 13 | } 14 | 15 | /** 16 | * a string describing the shape of a draggable point 17 | */ 18 | export type PointShape = 'rect' | 'circle'; 19 | 20 | export interface DraggablePointConfig { 21 | width?: number; 22 | height?: number; 23 | color?: string; 24 | shape?: 'rect' | 'circle'; 25 | limitRoles?: RolesArray; 26 | startPosition?: XYPosition; 27 | } 28 | 29 | /** 30 | * describes a position on a 2d pane 31 | */ 32 | export interface XYPosition { 33 | x: number; 34 | y: number; 35 | } 36 | 37 | /** 38 | * describes o draggable point config options 39 | */ 40 | export interface PointOptions { 41 | width: number; 42 | height: number; 43 | color: string; 44 | shape: PointShape; 45 | } 46 | 47 | export interface LimitException { 48 | exceeds: boolean; 49 | resetCoefficients: { 50 | x: 1 | 0 | -1 51 | y: 1 | 0 | -1 52 | }; 53 | resetCoordinates: { 54 | x: number; 55 | y: number; 56 | }; 57 | } 58 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/PublicModels.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * describes a state object for the OpenCV module 3 | */ 4 | import {PointShape} from './PrivateModels'; 5 | 6 | export interface OpenCVState { 7 | ready: boolean; 8 | loading: boolean; 9 | error: boolean; 10 | state: string; 11 | } 12 | 13 | /** 14 | * describes an object with width and height properties 15 | */ 16 | export interface ImageDimensions { 17 | width: number; 18 | height: number; 19 | } 20 | 21 | /** 22 | * describes a configuration object for the editor 23 | */ 24 | export interface DocScannerConfig { 25 | /** 26 | * max dimensions of output image. if set to zero will not resize the image 27 | */ 28 | maxImageDimensions?: ImageDimensions; 29 | /** 30 | * background color of the main editor div 31 | */ 32 | editorBackgroundColor?: string; 33 | /** 34 | * css properties for the main editor div 35 | */ 36 | editorDimensions?: { width: string; height: string; }; 37 | /** 38 | * css that will be added to the main div of the editor component 39 | */ 40 | extraCss?: { [key: string]: string | number }; 41 | /** 42 | * material design theme color name 43 | */ 44 | buttonThemeColor?: 'primary' | 'warn' | 'accent'; 45 | /** 46 | * icon for the button that completes the editing and emits the edited image 47 | */ 48 | exportImageIcon?: string; 49 | /** 50 | * color of the crop tool (points and connecting lines) 51 | */ 52 | cropToolColor?: string; 53 | /** 54 | * shape of the crop tool points 55 | */ 56 | cropToolShape?: PointShape; 57 | /** 58 | * width and height of the crop tool points 59 | */ 60 | cropToolDimensions?: ImageDimensions; 61 | /** 62 | * weight of the crop tool's connecting lines 63 | */ 64 | cropToolLineWeight?: number; 65 | /** 66 | * max width of the preview pane 67 | */ 68 | maxPreviewWidth?: number; 69 | } 70 | 71 | /** 72 | * describes a configuration object for the OpenCV service 73 | */ 74 | export interface OpenCVConfig { 75 | /** 76 | * path to the directory containing the OpenCV files, in the form of '/path/to/' 77 | * directory must contain the the following files: 78 | * -- 79 | * ----opencv.js 80 | * ----opencv_js.wasm 81 | */ 82 | openCVDirPath?: string; 83 | /** 84 | * additional callback that will run when OpenCv has finished loading and parsing 85 | */ 86 | runOnOpenCVInit?: Function; 87 | } 88 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/components/draggable-point/ngx-draggable-point.component.html: -------------------------------------------------------------------------------- 1 |
9 |
10 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/components/draggable-point/ngx-draggable-point.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, Input} from '@angular/core'; 2 | import {LimitsService, PointPositionChange, PositionChangeData} from '../../services/limits.service'; 3 | import {ImageDimensions} from '../../PublicModels'; 4 | import {LimitException, XYPosition} from '../../PrivateModels'; 5 | 6 | @Component({ 7 | selector: 'ngx-draggable-point', 8 | templateUrl: './ngx-draggable-point.component.html', 9 | }) 10 | export class NgxDraggablePointComponent implements AfterViewInit { 11 | @Input() width = 10; 12 | @Input() height = 10; 13 | @Input() color = '#3cabe2'; 14 | @Input() shape: 'rect' | 'circle' = 'rect'; 15 | @Input() pointOptions: 'rect' | 'circle' = 'rect'; 16 | @Input() limitRoles: Array<'left'|'right'|'top'|'bottom'>; 17 | @Input() startPosition: XYPosition; 18 | @Input() container: HTMLElement; 19 | @Input() private _currentPosition: XYPosition; 20 | position: XYPosition = { 21 | x: 0, 22 | y: 0 23 | }; 24 | private _paneDimensions: ImageDimensions; 25 | resetPosition: XYPosition; 26 | 27 | constructor(private limitsService: LimitsService) {} 28 | 29 | ngAfterViewInit() { 30 | Object.keys(this.pointOptions).forEach(key => { 31 | this[key] = this.pointOptions[key]; 32 | }); 33 | // subscribe to pane dimensions changes 34 | this.limitsService.paneDimensions.subscribe(dimensions => { 35 | if (dimensions.width > 0 && dimensions.width > 0) { 36 | this._paneDimensions = { 37 | width: dimensions.width, 38 | height: dimensions.height 39 | }; 40 | this.position = this.getInitialPosition(dimensions); 41 | this.limitsService.positionChange(new PositionChangeData(this.position, this.limitRoles)); 42 | } 43 | }); 44 | // subscribe to external reposition events 45 | this.limitsService.repositionEvent.subscribe(positions => { 46 | if (positions.length > 0) { 47 | this.externalReposition(positions); 48 | } 49 | }); 50 | } 51 | 52 | /** 53 | * returns a css style object for the point 54 | */ 55 | pointStyle() { 56 | return { 57 | width: this.width + 'px', 58 | height: this.height + 'px', 59 | 'background-color': this.color, 60 | 'border-radius': this.shape === 'circle' ? '100%' : 0, 61 | position: 'absolute' 62 | }; 63 | } 64 | 65 | /** 66 | * registers a position change on the limits service, and adjusts position if necessary 67 | * @param position - the current position of the point 68 | */ 69 | positionChange(position: XYPosition) { 70 | const positionChangeData = new PositionChangeData(position, this.limitRoles); 71 | const limitException = this.limitsService.exceedsLimit(positionChangeData); 72 | if (limitException.exceeds) { 73 | // if exceeds limits, reposition 74 | this.resetPosition = limitException.resetCoordinates; 75 | } else { 76 | this.limitsService.positionChange(positionChangeData); 77 | this._currentPosition = position; 78 | } 79 | } 80 | 81 | /** 82 | * adjusts the position of the point after a limit exception 83 | */ 84 | private adjustPosition(limitException: LimitException) { 85 | const newPosition = { 86 | x: 0, 87 | y: 0 88 | }; 89 | Object.keys(this.startPosition).forEach(axis => { 90 | newPosition[axis] = limitException.resetCoordinates[axis] + limitException.resetCoefficients[axis]; 91 | }); 92 | this.position = newPosition; 93 | this.limitsService.positionChange(new PositionChangeData(this.position, this.limitRoles)); 94 | } 95 | 96 | /** 97 | * called on movement end, checks if last position exceeded the limits ad adjusts 98 | */ 99 | movementEnd(position: XYPosition) { 100 | let positionChangeData = new PositionChangeData(position, this.limitRoles); 101 | const limitException = this.limitsService.exceedsLimit(positionChangeData); 102 | if (limitException.exceeds) { 103 | this.resetPosition = limitException.resetCoordinates; 104 | if (limitException.exceeds) { 105 | this.adjustPosition(limitException); 106 | positionChangeData = new PositionChangeData(this.position, this.limitRoles); 107 | this.limitsService.updatePosition(positionChangeData); 108 | } 109 | } 110 | } 111 | 112 | /** 113 | * calculates the initial positions of the point by it's roles 114 | * @param dimensions - dimensions of the pane in which the point is located 115 | */ 116 | private getInitialPosition(dimensions: ImageDimensions) { 117 | return { 118 | x: this.limitRoles.includes('left') ? 0 : dimensions.width - this.width / 2, 119 | y: this.limitRoles.includes('top') ? 0 : dimensions.height - this.height / 2 120 | }; 121 | } 122 | 123 | /** 124 | * repositions the point after an external reposition event 125 | * @param positions - an array of all points on the pane 126 | */ 127 | private externalReposition(positions: Array) { 128 | positions.forEach(position => { 129 | if (this.limitsService.compareArray(this.limitRoles, position.roles)) { 130 | position = this.enforcePaneLimits(position); 131 | this.position = { 132 | x: position.x, 133 | y: position.y 134 | }; 135 | } 136 | }); 137 | } 138 | 139 | /** 140 | * returns a new point position if the movement exceeded the pane limit 141 | */ 142 | private enforcePaneLimits(position: PointPositionChange): PointPositionChange { 143 | if (this._paneDimensions.width === 0 || this._paneDimensions.height === 0) { 144 | return position; 145 | } else { 146 | if (position.x > this._paneDimensions.width) { position.x = this._paneDimensions.width; } 147 | if (position.x < 0) { position.x = 1; } 148 | if (position.y > this._paneDimensions.height) { position.y = this._paneDimensions.height; } 149 | if (position.y < 0) { position.y = 1; } 150 | } 151 | return position; 152 | } 153 | } 154 | 155 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/components/filter-menu/ngx-filter-menu.component.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/components/filter-menu/ngx-filter-menu.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, EventEmitter, Inject, Output} from '@angular/core'; 2 | import {EditorActionButton} from '../../PrivateModels'; 3 | import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef} from '@angular/material/bottom-sheet'; 4 | 5 | @Component({ 6 | selector: 'ngx-filter-menu', 7 | templateUrl: './ngx-filter-menu.component.html', 8 | }) 9 | export class NgxFilterMenuComponent { 10 | filterOptions: Array = [ 11 | { 12 | name: 'default', 13 | icon: 'filter_b_and_w', 14 | action: (filter) => { 15 | this.filterSelected.emit(filter); 16 | }, 17 | text: 'B&W' 18 | }, 19 | { 20 | name: 'bw2', 21 | icon: 'filter_b_and_w', 22 | action: (filter) => { 23 | this.filterSelected.emit(filter); 24 | }, 25 | text: 'B&W 2' 26 | }, 27 | { 28 | name: 'bw3', 29 | icon: 'blur_on', 30 | action: (filter) => { 31 | this.filterSelected.emit(filter); 32 | }, 33 | text: 'B&W 3' 34 | }, 35 | { 36 | name: 'magic_color', 37 | icon: 'filter_vintage', 38 | action: (filter) => { 39 | this.filterSelected.emit(filter); 40 | }, 41 | text: 'Magic Color' 42 | }, 43 | { 44 | name: 'original', 45 | icon: 'crop_original', 46 | action: (filter) => { 47 | this.filterSelected.emit(filter); 48 | }, 49 | text: 'Original' 50 | }, 51 | ]; 52 | @Output() filterSelected: EventEmitter = new EventEmitter(); 53 | selectOption(optionName) { 54 | this.data.filter = optionName; 55 | this.bottomSheetRef.dismiss(); 56 | } 57 | 58 | constructor(private bottomSheetRef: MatBottomSheetRef, 59 | @Inject(MAT_BOTTOM_SHEET_DATA) public data: any 60 | ) {} 61 | 62 | } 63 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/components/image-editor/ngx-doc-scanner.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 17 | 21 | 22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/components/image-editor/ngx-doc-scanner.component.scss: -------------------------------------------------------------------------------- 1 | .editor-actions { 2 | padding: 12px; 3 | button { 4 | margin: 5px; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/components/shape-outline/ngx-shape-outline.component.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/components/shape-outline/ngx-shape-outline.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, Input, ViewChild} from '@angular/core'; 2 | import {LimitsService, PointPositionChange} from '../../services/limits.service'; 3 | import {ImageDimensions} from '../../PublicModels'; 4 | 5 | @Component({ 6 | selector: 'ngx-shape-outine', 7 | templateUrl: './ngx-shape-outline.component.html', 8 | }) 9 | export class NgxShapeOutlineComponent implements AfterViewInit { 10 | 11 | @Input() color = '#3cabe2'; 12 | @Input() weight: number; 13 | @Input() dimensions: ImageDimensions; 14 | @ViewChild('outline') canvas; 15 | 16 | private _points: Array; 17 | private _sortedPoints: Array; 18 | constructor(private limitsService: LimitsService) {} 19 | 20 | ngAfterViewInit() { 21 | // init drawing canvas dimensions 22 | this.canvas.nativeElement.width = this.dimensions.width; 23 | this.canvas.nativeElement.height = this.dimensions.height; 24 | this.limitsService.positions.subscribe(positions => { 25 | if (positions.length === 4) { 26 | this._points = positions; 27 | this.sortPoints(); 28 | this.clearCanvas(); 29 | this.drawShape(); 30 | } 31 | }); 32 | // subscribe to changes in the pane's dimensions 33 | this.limitsService.paneDimensions.subscribe(dimensions => { 34 | this.clearCanvas(); 35 | this.canvas.nativeElement.width = dimensions.width; 36 | this.canvas.nativeElement.height = dimensions.height; 37 | }); 38 | // subscribe to reposition events 39 | this.limitsService.repositionEvent.subscribe( positions => { 40 | if (positions.length === 4) { 41 | setTimeout( () => { 42 | this.clearCanvas(); 43 | this.sortPoints(); 44 | this.drawShape(); 45 | }, 10); 46 | } 47 | }); 48 | } 49 | 50 | /** 51 | * clears the shape canvas 52 | */ 53 | private clearCanvas() { 54 | const canvas = this.canvas.nativeElement; 55 | const ctx = canvas.getContext('2d'); 56 | ctx.clearRect(0, 0, this.dimensions.width, this.dimensions.height); 57 | } 58 | 59 | /** 60 | * sorts the array of points according to their clockwise alignment 61 | */ 62 | private sortPoints() { 63 | const _points = Array.from(this._points); 64 | const sortedPoints = []; 65 | 66 | const sortOrder = { 67 | vertical: ['top', 'top', 'bottom', 'bottom'], 68 | horizontal: ['left', 'right', 'right', 'left'] 69 | }; 70 | 71 | for (let i = 0; i < 4; i++) { 72 | const roles = Array.from([sortOrder.vertical[i], sortOrder.horizontal[i]]); 73 | sortedPoints.push(_points.filter((point) => { 74 | return this.limitsService.compareArray(point.roles, roles); 75 | })[0]); 76 | 77 | } 78 | this._sortedPoints = sortedPoints; 79 | } 80 | 81 | /** 82 | * draws a line between the points according to their order 83 | */ 84 | private drawShape() { 85 | const canvas = this.canvas.nativeElement; 86 | const ctx = canvas.getContext('2d'); 87 | ctx.lineWidth = this.weight; 88 | ctx.strokeStyle = this.color; 89 | ctx.beginPath(); 90 | this._sortedPoints.forEach((point, index) => { 91 | if (index === 0) { 92 | ctx.moveTo(point.x, point.y); 93 | } 94 | if (index !== this._sortedPoints.length - 1) { 95 | const nextPoint = this._sortedPoints[index + 1]; 96 | ctx.lineTo(nextPoint.x, nextPoint.y); 97 | } else { 98 | ctx.closePath(); 99 | } 100 | }); 101 | ctx.stroke(); 102 | } 103 | } 104 | 105 | 106 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/lib/ngx-document-scanner.module.ts: -------------------------------------------------------------------------------- 1 | import {ModuleWithProviders, NgModule} from '@angular/core'; 2 | import {NgxDraggablePointComponent} from './components/draggable-point/ngx-draggable-point.component'; 3 | import {NgxFilterMenuComponent} from './components/filter-menu/ngx-filter-menu.component'; 4 | import {NgxShapeOutlineComponent} from './components/shape-outline/ngx-shape-outline.component'; 5 | import {NgxDocScannerComponent} from './components/image-editor/ngx-doc-scanner.component'; 6 | import {LimitsService} from './services/limits.service'; 7 | import {FlexLayoutModule} from '@angular/flex-layout'; 8 | import {AngularDraggableModule} from 'angular2-draggable'; 9 | import {CommonModule} from '@angular/common'; 10 | import {OpenCVConfig} from './PublicModels'; 11 | import {NgxOpenCVModule} from 'ngx-opencv'; 12 | import { MatButtonModule } from '@angular/material/button'; 13 | import { MatIconModule } from '@angular/material/icon'; 14 | import { MatBottomSheetModule } from '@angular/material/bottom-sheet'; 15 | import { MatListModule } from '@angular/material/list'; 16 | import {NgxOpenCVService, OpenCvConfigToken} from 'ngx-opencv'; 17 | 18 | @NgModule({ 19 | declarations: [ 20 | NgxDraggablePointComponent, 21 | NgxFilterMenuComponent, 22 | NgxShapeOutlineComponent, 23 | NgxDocScannerComponent, 24 | ], 25 | imports: [ 26 | FlexLayoutModule, 27 | MatButtonModule, 28 | MatIconModule, 29 | MatBottomSheetModule, 30 | MatListModule, 31 | AngularDraggableModule, 32 | CommonModule, 33 | NgxOpenCVModule, 34 | ], 35 | exports: [ 36 | FlexLayoutModule, 37 | MatButtonModule, 38 | MatIconModule, 39 | MatBottomSheetModule, 40 | MatListModule, 41 | AngularDraggableModule, 42 | NgxDocScannerComponent, 43 | ], 44 | entryComponents: [ 45 | NgxFilterMenuComponent, 46 | ], 47 | providers: [ 48 | NgxOpenCVService, 49 | LimitsService, 50 | ] 51 | }) 52 | export class NgxDocumentScannerModule { 53 | static forRoot(config: OpenCVConfig): ModuleWithProviders { 54 | return { 55 | ngModule: NgxDocumentScannerModule, 56 | providers: [ 57 | { provide: OpenCvConfigToken, useValue: config }, 58 | ], 59 | }; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/public_api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of ngx-document-scanner 3 | */ 4 | 5 | export * from './lib/ngx-document-scanner.module'; 6 | export * from './lib/components/image-editor/ngx-doc-scanner.component'; 7 | export * from './lib/PublicModels'; 8 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'core-js/es7/reflect'; 4 | import 'zone.js/dist/zone'; 5 | import 'zone.js/dist/zone-testing'; 6 | import { getTestBed } from '@angular/core/testing'; 7 | import { 8 | BrowserDynamicTestingModule, 9 | platformBrowserDynamicTesting 10 | } from '@angular/platform-browser-dynamic/testing'; 11 | 12 | declare const require: any; 13 | 14 | // First, initialize the Angular testing environment. 15 | getTestBed().initTestEnvironment( 16 | BrowserDynamicTestingModule, 17 | platformBrowserDynamicTesting() 18 | ); 19 | // Then we find all the tests. 20 | const context = require.context('./', true, /\.spec\.ts$/); 21 | // And load the modules. 22 | context.keys().map(context); 23 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": [ 16 | "dom", 17 | "es2018" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "annotateForClosureCompiler": true, 22 | "skipTemplateCodegen": true, 23 | "strictMetadataEmit": true, 24 | "fullTemplateTypeCheck": true, 25 | "strictInjectionParameters": true, 26 | "enableResourceInlining": true, 27 | "enableIvy": false 28 | }, 29 | "exclude": [ 30 | "src/test.ts", 31 | "**/*.spec.ts" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-document-scanner/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "ngx", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "ngx", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/projects/ngx-ds-demo-app/src/app/app.component.scss -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async(() => { 6 | TestBed.configureTestingModule({ 7 | declarations: [ 8 | AppComponent 9 | ], 10 | }).compileComponents(); 11 | })); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.debugElement.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'ngx-ds-demo-app'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.debugElement.componentInstance; 22 | expect(app.title).toEqual('ngx-ds-demo-app'); 23 | }); 24 | 25 | it('should render title in a h1 tag', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.debugElement.nativeElement; 29 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to ngx-ds-demo-app!'); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.scss'] 7 | }) 8 | export class AppComponent { 9 | title = 'ngx-ds-demo-app'; 10 | } 11 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { AppComponent } from './app.component'; 4 | import {FormsModule} from '@angular/forms'; 5 | import {FlexLayoutModule} from '@angular/flex-layout'; 6 | import { MatButtonModule } from '@angular/material/button'; 7 | import { MatIconModule } from '@angular/material/icon'; 8 | import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; 9 | import { MatToolbarModule } from '@angular/material/toolbar'; 10 | import { MatSidenavModule } from '@angular/material/sidenav'; 11 | import { MatListModule } from '@angular/material/list'; 12 | 13 | import {NgxDocumentScannerModule} from 'ngx-document-scanner'; 14 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 15 | import { SideNavComponent } from './components/side-nav/side-nav.component'; 16 | import { LayoutModule } from '@angular/cdk/layout'; 17 | import { RoutingModule } from './routing.module'; 18 | import { DemoComponent } from './components/demo/demo.component'; 19 | import {OpenCVConfig} from 'ngx-document-scanner'; 20 | 21 | const openCvConfig: OpenCVConfig = { 22 | openCVDirPath: './assets/opencv' 23 | }; 24 | 25 | @NgModule({ 26 | declarations: [ 27 | AppComponent, 28 | SideNavComponent, 29 | DemoComponent 30 | ], 31 | imports: [ 32 | BrowserModule, 33 | FormsModule, 34 | FlexLayoutModule, 35 | MatButtonModule, 36 | MatIconModule, 37 | MatProgressSpinnerModule, 38 | NgxDocumentScannerModule.forRoot(openCvConfig), 39 | BrowserAnimationsModule, 40 | LayoutModule, 41 | MatToolbarModule, 42 | MatSidenavModule, 43 | MatListModule, 44 | RoutingModule, 45 | ], 46 | providers: [], 47 | bootstrap: [AppComponent] 48 | }) 49 | export class AppModule { } 50 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/components/demo/demo.component.html: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 |
13 |
20 |
21 | Drop an image here, or click to upload 22 | cloud_upload 23 |
24 |
25 |
26 | 27 | 28 | 29 |
33 | 34 |
35 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/components/demo/demo.component.scss: -------------------------------------------------------------------------------- 1 | $primary: rgb(63, 81, 181); 2 | $primary_trans: rgba(63, 81, 181, 0.15); 3 | 4 | .dropZone { 5 | border: 2px dashed $primary; 6 | width: 600px; 7 | height:300px; 8 | max-width: 98vw; 9 | max-height: 50vh; 10 | transition: 0.5s all; 11 | margin-top: auto; 12 | margin-bottom: auto; 13 | cursor: pointer; 14 | border-radius: 20px; 15 | padding: 18px; 16 | } 17 | 18 | .drop-hover { 19 | background-color: $primary_trans; 20 | } 21 | 22 | .inner-text { 23 | font-size: 27px; 24 | } 25 | 26 | $icon_size: 64px; 27 | .cloud-icon { 28 | font-size: $icon_size; 29 | margin-left: calc((100% - 64px) / 2) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/components/demo/demo.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DemoComponent } from './demo.component'; 4 | 5 | describe('DemoComponent', () => { 6 | let component: DemoComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ DemoComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DemoComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/components/demo/demo.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import {DocScannerConfig} from '../../../../../ngx-document-scanner/src/lib/PublicModels'; 3 | 4 | @Component({ 5 | selector: 'app-demo', 6 | templateUrl: './demo.component.html', 7 | styleUrls: ['./demo.component.scss'] 8 | }) 9 | export class DemoComponent { 10 | 11 | overZone = false; 12 | image: File; 13 | processing: boolean; 14 | test: boolean; 15 | config: DocScannerConfig = { 16 | editorBackgroundColor: '#fafafa', 17 | buttonThemeColor: 'primary', 18 | cropToolColor: '#ff4081', 19 | cropToolShape: 'rect', 20 | cropToolDimensions: { 21 | width: 16, 22 | height: 16 23 | }, 24 | exportImageIcon: 'cloud_download', 25 | editorDimensions: { 26 | width: '99vw', 27 | height: '82vh' 28 | }, 29 | extraCss: { 30 | position: 'absolute', 31 | top: 0, 32 | left: 0 33 | } 34 | }; 35 | 36 | constructor() {} 37 | 38 | // ******************* // 39 | // file input handlers // 40 | // ******************* // 41 | dropFile(event) { 42 | event.preventDefault(); 43 | event.stopPropagation(); 44 | if (event.dataTransfer.files.item(0)) { 45 | const file = event.dataTransfer.files.item(0); 46 | if (this.isImage(file)) { 47 | this.loadFile(file); 48 | } else { 49 | this.overZone = false; 50 | } 51 | } 52 | } 53 | 54 | loadFile(event: any) { 55 | this.processing = true; 56 | this.overZone = false; 57 | let f: File; 58 | if (event instanceof File) { 59 | f = event; 60 | } else { 61 | const files = event.target.files; 62 | f = files[0]; 63 | } 64 | if (this.isImage(f)) { 65 | this.image = f; 66 | } 67 | } 68 | 69 | isImage(file: File) { 70 | const types = [ 71 | 'image/png', 72 | 'image/jpeg', 73 | 'image/jpg', 74 | ]; 75 | return types.findIndex(type => { 76 | return type === file.type; 77 | }) !== -1; 78 | } 79 | 80 | 81 | // ******************************** // 82 | // bindings to doc scanner component// 83 | // ******************************** // 84 | // resets the file input when the user exits the editor 85 | exitEditor(message) { 86 | console.log(message); 87 | this.image = null; 88 | } 89 | 90 | // handles the result emitted by the editor 91 | editResult(result: Blob) { 92 | const link = document.createElement('a'); 93 | link.href = URL.createObjectURL(result); 94 | link.setAttribute('download', `edited_image_${new Date().toLocaleString()}.${this.image.type.split('/')[1]}`); 95 | link.click(); 96 | } 97 | 98 | // handles errors emitted by the component 99 | onError(err: Error) { 100 | console.error(err); 101 | } 102 | 103 | // handles changes in the editor state - is it processing or not 104 | editorState(processing) { 105 | this.processing = null; 106 | this.processing = processing; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/components/side-nav/side-nav.component.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | Ngx Document Scanner 6 | 7 | 8 | computer 9 | Demo App 10 | 11 | 12 | description 13 | Documentation 14 | 15 | 16 | 17 | Github 18 | 19 | 20 | 21 | npm 22 | 23 | 24 | 25 | Ngx-OpenCV 26 | 27 | 28 | 29 | 30 | 31 | 38 | Ngx Document Scanner 39 | 40 |
41 | 42 |
43 |
44 |
45 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/components/side-nav/side-nav.component.scss: -------------------------------------------------------------------------------- 1 | .sidenav-container { 2 | height: 100%; 3 | } 4 | 5 | .sidenav { 6 | width: 300px; 7 | } 8 | 9 | .sidenav .mat-toolbar { 10 | background: inherit; 11 | } 12 | 13 | .mat-toolbar.mat-primary { 14 | position: sticky; 15 | top: 0; 16 | z-index: 1; 17 | } 18 | 19 | .menu-text { 20 | margin-left: 16px; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/components/side-nav/side-nav.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { LayoutModule } from '@angular/cdk/layout'; 2 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 3 | import { NoopAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { MatToolbarModule } from '@angular/material/toolbar'; 5 | import { MatButtonModule } from '@angular/material/button'; 6 | import { MatIconModule } from '@angular/material/icon'; 7 | import { MatListModule } from '@angular/material/list'; 8 | import { MatSidenavModule } from '@angular/material/sidenav'; 9 | import { SideNavComponent } from './side-nav.component'; 10 | 11 | describe('SideNavComponent', () => { 12 | let component: SideNavComponent; 13 | let fixture: ComponentFixture; 14 | 15 | beforeEach(async(() => { 16 | TestBed.configureTestingModule({ 17 | declarations: [SideNavComponent], 18 | imports: [ 19 | NoopAnimationsModule, 20 | LayoutModule, 21 | MatButtonModule, 22 | MatIconModule, 23 | MatListModule, 24 | MatSidenavModule, 25 | MatToolbarModule, 26 | ] 27 | }).compileComponents(); 28 | })); 29 | 30 | beforeEach(() => { 31 | fixture = TestBed.createComponent(SideNavComponent); 32 | component = fixture.componentInstance; 33 | fixture.detectChanges(); 34 | }); 35 | 36 | it('should compile', () => { 37 | expect(component).toBeTruthy(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/components/side-nav/side-nav.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, EventEmitter, Output, ViewChild} from '@angular/core'; 2 | import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | import {MatToolbar} from '@angular/material/toolbar'; 6 | 7 | @Component({ 8 | selector: 'app-side-nav', 9 | templateUrl: './side-nav.component.html', 10 | styleUrls: ['./side-nav.component.scss'], 11 | }) 12 | export class SideNavComponent implements AfterViewInit { 13 | 14 | topMargin = '25px'; 15 | outletCss: {[key: string]: any}; 16 | @ViewChild('toolbar') toolbar: MatToolbar; 17 | @Output() outletHeight: EventEmitter = new EventEmitter(); 18 | 19 | 20 | isHandset$: Observable = this.breakpointObserver.observe(Breakpoints.Handset) 21 | .pipe( 22 | map(result => result.matches) 23 | ); 24 | 25 | constructor(private breakpointObserver: BreakpointObserver) { 26 | } 27 | 28 | ngAfterViewInit() { 29 | this.topMargin = this.toolbar._elementRef.nativeElement.clientHeight + 'px'; 30 | setTimeout(() => { 31 | this.outletCss = { 32 | width: '100vw', 33 | 'max-width': '100vw', 34 | right: 0, 35 | }; 36 | }, 10); 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/app/routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import {RouterModule, Routes} from '@angular/router'; 3 | import {DemoComponent} from './components/demo/demo.component'; 4 | 5 | const routes: Routes = [ 6 | { 7 | path: '', 8 | component: DemoComponent 9 | }, 10 | { 11 | path: 'demo', 12 | component: DemoComponent 13 | }, 14 | { 15 | path: 'docs', 16 | component: DemoComponent 17 | }, 18 | ]; 19 | 20 | @NgModule({ 21 | imports: [RouterModule.forRoot(routes)], 22 | exports: [RouterModule] 23 | }) 24 | export class RoutingModule { } 25 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/projects/ngx-ds-demo-app/src/assets/.gitkeep -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/assets/git.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/projects/ngx-ds-demo-app/src/assets/git.jpg -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/assets/npm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/projects/ngx-ds-demo-app/src/assets/npm.jpg -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/assets/opencv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/projects/ngx-ds-demo-app/src/assets/opencv.png -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/assets/opencv/opencv_js.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/projects/ngx-ds-demo-app/src/assets/opencv/opencv_js.wasm -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/projects/ngx-ds-demo-app/src/favicon.ico -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NgxDsDemoApp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** 38 | * If the application will be indexed by Google Search, the following is required. 39 | * Googlebot uses a renderer based on Chrome 41. 40 | * https://developers.google.com/search/docs/guides/rendering 41 | **/ 42 | // import 'core-js/es6/array'; 43 | 44 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 45 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 46 | 47 | /** IE10 and IE11 requires the following for the Reflect API. */ 48 | // import 'core-js/es6/reflect'; 49 | 50 | /** 51 | * Web Animations `@angular/platform-browser/animations` 52 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 53 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 54 | **/ 55 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 56 | 57 | /** 58 | * By default, zone.js will patch all possible macroTask and DomEvents 59 | * user can disable parts of macroTask/DomEvents patch by setting following flags 60 | */ 61 | 62 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 63 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 64 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 65 | 66 | /* 67 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 68 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 69 | */ 70 | // (window as any).__Zone_enable_cross_context_check = true; 71 | 72 | /*************************************************************************************************** 73 | * Zone JS is required by default for Angular itself. 74 | */ 75 | import 'zone.js/dist/zone'; // Included with Angular CLI. 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | 4 | html, body { height: 100%; } 5 | body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } 6 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /projects/ngx-ds-demo-app/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-opencv/README.md: -------------------------------------------------------------------------------- 1 | # Ngx OpenCv 2 | NgxOpenCV is a lightweight angular service for integrating OpenCV.js [WASM](https://webassembly.org/) in Angular 2+ applications. 3 | OpenCV on WASM offers near-native performance in web-based applications; 4 | The service loads the library in the angular zone, thus enabling better control over it's state. 5 | Note that there are a few extra steps required to configure the component other than installing the package from npm. 6 | 7 | ## Live Demo 8 | View a live demo of an implementation of this library in another project - **[here](https://roiperlman.github.io/ngx-document-scanner)** 9 | 10 | ## Installation & Setup 11 | install the package via npm 12 | 13 | npm install ngx-opencv --save 14 | 15 | copy the opencv.js files to your assets folder (or any other folder). you can build the files yourself ([instructions on the OpenCV site](https://docs.opencv.org/3.4/d4/da1/tutorial_js_setup.html)), or download them from this package's [repository](https://github.com/roiperlman/ngx-document-scanner). 16 | both opencv.js & opencv_js.wasm need to placed in the same folder. 17 | 18 | import the module to your `app.module`. you'll need to configure the location of the open cv files. 19 | 20 | import {OpenCVConfig} from 'ngx-document-scanner'; 21 | import {NgxOpencv} from 'ngx-opencv'; 22 | 23 | // set the location of the OpenCV files 24 | const openCVConfig: OpenCVConfig = { 25 | openCVDirPath: '/assets/opencv' 26 | }; 27 | 28 | @NgModule({ imports: [ 29 | NgxOpenCVModule.forRoot(openCVConfig) 30 | ], 31 | bootstrap: [AppComponent] 32 | }) 33 | export class AppModule { } 34 | 35 | 36 | You'll need to set 'cv' as a global variable, or on the component level: 37 | 38 | declare var cv: any; 39 | 40 | this is very important to avoid TypeScript errors. 41 | 42 | ## Usage 43 | 44 | Inject NgxOpenCVService to the constructor of your component / service etc. and subscribe to the cvState observable. 45 | 46 | constructor(private ngxOpenCv: NgxOpenCVService) { 47 | // subscribe to status of OpenCV module 48 | this.ngxOpenCv.cvState.subscribe( 49 | (cvState: OpenCVState) => { 50 | // do something with the state string 51 | this.cvState = cvState.state; 52 | if (cvState.error) { 53 | // handle errors 54 | } else if (cvState.loading) { 55 | // e.g. show loading indicator 56 | } else if (cvState.ready) { 57 | // do image processing stuff 58 | } 59 | }); 60 | } 61 | 62 | **Note that loading and parsing of the OpenCV library is done synchronously, and might take some time while blocking execution of other processes, depending on client's device.** Therefore it's recommended to bind a loading indicator to the state observable. 63 | 64 | The observable emits an `OpenCVState` object when changes occur, with the following properties: 65 | 66 | | property |type | description | 67 | |--|--|--| 68 | | loading | boolean | true when loading of the opencv lib. is initiated, until it's completion callback is fires or the listener pick up an error | 69 | | error | boolean | true when an error is picked up by the listener | 70 | | ready | boolean | true when loading **and parsing** was copleted | 71 | | state | string | indicates the current state of the module as a string | 72 | 73 | 74 | ## Configuration Options 75 | You can configure the service with the OpenCVConfig object 76 | 77 | import {OpenCvConfig} from 'ngx-document-scanner'; 78 | 79 | | property | type | description | 80 | |--|--|--| 81 | |openCVDirPath| string | path to the directory containing the OpenCV files, in the form of `'/path/to/[opencv directory]'`. directory must contain `opencv.js` & `opencv_js.wasm`.| 82 | |runOnOpenCVInit| Function| additional callback that will run when OpenCv has finished loading and parsing. callback runs in the angular zone in the context of the service.| 83 | 84 | -------------------------------------------------------------------------------- /projects/ngx-opencv/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /projects/ngx-opencv/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/ngx-opencv", 4 | "lib": { 5 | "entryFile": "src/public_api.ts" 6 | }, 7 | "whitelistedNonPeerDependencies": ["."] 8 | } 9 | -------------------------------------------------------------------------------- /projects/ngx-opencv/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-opencv", 3 | "version": "1.0.5", 4 | "peerDependencies": { 5 | "@angular/common": "^9.1.9", 6 | "@angular/core": "^9.1.9", 7 | "ngx-opencv": "^2.0.1" 8 | }, 9 | "keywords": [ 10 | "opencv", 11 | "camscanner", 12 | "scanner", 13 | "image processing", 14 | "document", 15 | "angular", 16 | "OpenCVJS", 17 | "image manipulation", 18 | "crop" 19 | ], 20 | "description": "Angular service for implementing the OpenCV library in angular based applications", 21 | "author": "RoiP", 22 | "license": "MIT", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/roiperlman/ngx-document-scanner.git" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /projects/ngx-opencv/src/lib/models.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * describes a configuration object for the OpenCV service 3 | */ 4 | export interface OpenCVConfig { 5 | /** 6 | * path to the directory containing the OpenCV files, in the form of '/path/to/' 7 | * directory must contain the the following files: 8 | * -- 9 | * ----opencv.js 10 | * ----opencv_js.wasm 11 | */ 12 | openCVDirPath?: string; 13 | /** 14 | * additional callback that will run when OpenCv has finished loading and parsing 15 | */ 16 | runOnOpenCVInit?: Function; 17 | } 18 | 19 | export interface OpenCVState { 20 | ready: boolean; 21 | loading: boolean; 22 | error: boolean; 23 | state: string; 24 | } 25 | -------------------------------------------------------------------------------- /projects/ngx-opencv/src/lib/ngx-open-cv.service.ts: -------------------------------------------------------------------------------- 1 | import {Inject, Injectable, InjectionToken, NgZone} from '@angular/core'; 2 | import {BehaviorSubject} from 'rxjs'; 3 | import {OpenCVConfig, OpenCVState} from './models'; 4 | 5 | export const OpenCvConfigToken = new InjectionToken('OpenCV config object token'); 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class NgxOpenCVService { 11 | 12 | cvState = new BehaviorSubject({ 13 | ready: false, 14 | error: false, 15 | loading: true, 16 | state: 'loading' 17 | }); 18 | configModule: OpenCvConfigModule; 19 | 20 | constructor(@Inject(OpenCvConfigToken) options: OpenCVConfig, private _ngZone: NgZone) { 21 | if (!options) { 22 | options = {}; 23 | } 24 | this.configModule = this.generateConfigModule(options); 25 | this.loadOpenCv(); 26 | } 27 | 28 | /** 29 | * load the OpenCV script 30 | */ 31 | loadOpenCv() { 32 | this.cvState.next( this.newState('loading')); 33 | // create global module variable 34 | window['Module'] = this.configModule; 35 | 36 | // create script element and set attributes 37 | const script = document.createElement('script'); 38 | script.setAttribute('async', ''); 39 | script.setAttribute('type', 'text/javascript'); 40 | 41 | // listen for errors 42 | script.addEventListener('error', () => { 43 | const err = new Error('Failed to load ' + this.configModule.scriptUrl); 44 | this.cvState.next(this.newState('error')); 45 | this.cvState.error(err); 46 | }, {passive: true}); 47 | 48 | // set script url 49 | script.src = this.configModule.scriptUrl; 50 | // insert script as first script tag 51 | const node = document.getElementsByTagName('script')[0]; 52 | if (node) { 53 | node.parentNode.insertBefore(script, node); 54 | } else { 55 | document.head.appendChild(script); 56 | } 57 | } 58 | 59 | /** 60 | * generates a new state object 61 | * @param change - the new state of the module 62 | */ 63 | private newState(change: 'loading'|'ready'|'error'): OpenCVState { 64 | const newStateObj: OpenCVState = { 65 | ready: false, 66 | loading: false, 67 | error: false, 68 | state: '' 69 | }; 70 | Object.keys(newStateObj).forEach(key => { 71 | if (key !== 'state') { 72 | if (key === change) { 73 | newStateObj[key] = true; 74 | newStateObj.state = key; 75 | } else { 76 | newStateObj[key] = false; 77 | } 78 | } 79 | }); 80 | return newStateObj; 81 | } 82 | 83 | /** 84 | * generates a config module for the global Module object 85 | * @param options - configuration options 86 | */ 87 | private generateConfigModule(options: OpenCVConfig): OpenCvConfigModule { 88 | return { 89 | scriptUrl: options.openCVDirPath ? `${options.openCVDirPath}/opencv.js` : `/assets/opencv/opencv.js`, 90 | wasmBinaryFile: 'opencv_js.wasm', 91 | usingWasm: true, 92 | onRuntimeInitialized: () => { 93 | this._ngZone.run(() => { 94 | console.log('openCV Ready'); 95 | this.cvState.next(this.newState('ready')); 96 | if (options.runOnOpenCVInit) { 97 | options.runOnOpenCVInit(); 98 | } 99 | }); 100 | } 101 | }; 102 | } 103 | } 104 | 105 | /** 106 | * describes the global Module object that is used to initiate OpenCV.js 107 | */ 108 | interface OpenCvConfigModule { 109 | scriptUrl: string; 110 | wasmBinaryFile: string; 111 | usingWasm: boolean; 112 | onRuntimeInitialized: Function; 113 | } 114 | 115 | -------------------------------------------------------------------------------- /projects/ngx-opencv/src/lib/ngx-opencv.module.ts: -------------------------------------------------------------------------------- 1 | import {ModuleWithProviders, NgModule} from '@angular/core'; 2 | import {OpenCVConfig} from './models'; 3 | import {NgxOpenCVService, OpenCvConfigToken} from './ngx-open-cv.service'; 4 | 5 | 6 | @NgModule({ 7 | declarations: [], 8 | exports: [], 9 | providers: [NgxOpenCVService] 10 | }) 11 | export class NgxOpenCVModule { 12 | static forRoot(config: OpenCVConfig): ModuleWithProviders { 13 | return { 14 | ngModule: NgxOpenCVModule, 15 | providers: [{ provide: OpenCvConfigToken, useValue: config }] 16 | }; 17 | } 18 | } 19 | 20 | const a = 0; 21 | -------------------------------------------------------------------------------- /projects/ngx-opencv/src/public_api.ts: -------------------------------------------------------------------------------- 1 | 2 | export * from './lib/ngx-opencv.module'; 3 | export * from './lib/models'; 4 | export * from './lib/ngx-open-cv.service'; 5 | -------------------------------------------------------------------------------- /projects/ngx-opencv/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": [ 16 | "dom", 17 | "es2018" 18 | ] 19 | }, 20 | "angularCompilerOptions": { 21 | "annotateForClosureCompiler": true, 22 | "skipTemplateCodegen": true, 23 | "strictMetadataEmit": true, 24 | "fullTemplateTypeCheck": true, 25 | "strictInjectionParameters": true, 26 | "enableResourceInlining": true, 27 | "enableIvy": false 28 | }, 29 | "exclude": [ 30 | "src/test.ts", 31 | "**/*.spec.ts" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /projects/ngx-opencv/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/ngx-opencv/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "ngx", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "ngx", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'doc-scanner'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('doc-scanner'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to doc-scanner!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import {DraggablePointConfig} from './components/draggable-point/draggable-point.component'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.scss'] 8 | }) 9 | export class AppComponent { 10 | title = 'doc-scanner'; 11 | points: Array = [ 12 | { 13 | startPosition: { 14 | x: 0, 15 | y: 0 16 | } 17 | }, 18 | { 19 | startPosition: { 20 | x: 100, 21 | y: 0 22 | } 23 | }, 24 | { 25 | startPosition: { 26 | x: 0, 27 | y: 100 28 | } 29 | }, 30 | { 31 | startPosition: { 32 | x: 100, 33 | y: 100 34 | } 35 | }, 36 | ]; 37 | image: File; 38 | 39 | addPoint() { 40 | this.points.push({width: 15, height: 15}); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/app/components/draggable-point/draggable-point.component.html: -------------------------------------------------------------------------------- 1 |
10 |
11 | -------------------------------------------------------------------------------- /src/app/components/draggable-point/draggable-point.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/src/app/components/draggable-point/draggable-point.component.scss -------------------------------------------------------------------------------- /src/app/components/draggable-point/draggable-point.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DraggablePointComponent } from './draggable-point.component'; 4 | 5 | describe('DraggablePointComponent', () => { 6 | let component: DraggablePointComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ DraggablePointComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DraggablePointComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/draggable-point/draggable-point.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, Input, OnInit, Output, ViewChild} from '@angular/core'; 2 | import {AreaLimits, LimitException, LimitsService, PointPositionChange} from '../../services/limits.service'; 3 | 4 | @Component({ 5 | selector: 'app-draggable-point', 6 | templateUrl: './draggable-point.component.html', 7 | styleUrls: ['./draggable-point.component.scss'] 8 | }) 9 | export class DraggablePointComponent implements AfterViewInit { 10 | @Input() width = 10; 11 | @Input() height = 10; 12 | @Input() color = '#3cabe2'; 13 | @Input() shape: 'rect' | 'circle' = 'rect'; 14 | @Input() limitRoles: Array<'left'|'right'|'top'|'bottom'>; 15 | @Input() startPosition: XYPosition; 16 | @Input() container: HTMLElement; 17 | @Input() 18 | set rotation(direction) { 19 | const rotated = this.limitsService.rotateClockwise(new PositionChangeData(this._currentPosition, this.limitRoles)); 20 | this.limitRoles = rotated.roles; 21 | this.position.x = rotated.x; 22 | this.position.y = rotated.y; 23 | } 24 | position: XYPosition = { 25 | x: 0, 26 | y: 0 27 | }; 28 | private _currentPosition: XYPosition; 29 | resetPosition: XYPosition; 30 | 31 | constructor(private limitsService: LimitsService) { 32 | } 33 | 34 | ngAfterViewInit() { 35 | this.position = this.startPosition; 36 | // Object.assign(this.position, this.startPosition); 37 | this.limitsService.positionChange(new PositionChangeData(this.startPosition, this.limitRoles)); 38 | } 39 | 40 | pointStyle() { 41 | return { 42 | width: this.width + 'px', 43 | height: this.height + 'px', 44 | 'background-color': this.color, 45 | 'border-radius': this.shape === 'circle' ? '100%' : 0, 46 | position: 'absolute' 47 | }; 48 | } 49 | 50 | positionChange(position: XYPosition) { 51 | const positionChangeData = new PositionChangeData(position, this.limitRoles); 52 | const limitException = this.limitsService.exceedsLimit(positionChangeData); 53 | if (limitException.exceeds) { 54 | this.resetPosition = limitException.resetCoordinates; 55 | // this.adjustPosition(limitException, position); 56 | } else { 57 | this.limitsService.positionChange(positionChangeData); 58 | this._currentPosition = position; 59 | } 60 | } 61 | 62 | adjustPosition(limitException: LimitException) { 63 | const newPosition = { 64 | x: 0, 65 | y: 0 66 | }; 67 | Object.keys(this.startPosition).forEach(axis => { 68 | 69 | newPosition[axis] = limitException.resetCoordinates[axis] + limitException.resetCoefficients[axis] * this.width; 70 | }); 71 | this.position = newPosition; 72 | this.limitsService.positionChange(new PositionChangeData(this.position, this.limitRoles)); 73 | console.log(this.position); 74 | } 75 | 76 | movementEnd(position: XYPosition) { 77 | let positionChangeData = new PositionChangeData(position, this.limitRoles); 78 | const limitException = this.limitsService.exceedsLimit(positionChangeData); 79 | if (limitException.exceeds) { 80 | this.resetPosition = limitException.resetCoordinates; 81 | if (limitException.exceeds) { 82 | this.adjustPosition(limitException); 83 | positionChangeData = new PositionChangeData(this.position, this.limitRoles); 84 | this.limitsService.updatePosition(positionChangeData); 85 | } 86 | } 87 | } 88 | 89 | test() { 90 | console.log(this); 91 | } 92 | } 93 | 94 | 95 | export interface DraggablePointConfig { 96 | width?: number; 97 | height?: number; 98 | color?: string; 99 | shape?: 'rect' | 'circle'; 100 | limitRoles?: RolesArray; 101 | startPosition?: XYPosition; 102 | } 103 | 104 | export interface XYPosition { 105 | x: number; 106 | y: number; 107 | } 108 | 109 | export class PositionChangeData implements PointPositionChange { 110 | x: number; 111 | y: number; 112 | roles: RolesArray; 113 | 114 | constructor(position: XYPosition, roles: RolesArray) { 115 | this.x = position.x; 116 | this.y = position.y; 117 | this.roles = roles; 118 | } 119 | } 120 | 121 | export type Direction = 'left'|'right'|'top'|'bottom'; 122 | export type RolesArray = Array; 123 | -------------------------------------------------------------------------------- /src/app/components/image-container/image-container.component.html: -------------------------------------------------------------------------------- 1 |

2 | image-container works! 3 |

4 | -------------------------------------------------------------------------------- /src/app/components/image-container/image-container.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/src/app/components/image-container/image-container.component.scss -------------------------------------------------------------------------------- /src/app/components/image-container/image-container.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ImageContainerComponent } from './image-container.component'; 4 | 5 | describe('ImageContainerComponent', () => { 6 | let component: ImageContainerComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ImageContainerComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ImageContainerComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/image-editor/image-editor.component.scss: -------------------------------------------------------------------------------- 1 | .editor-actions { 2 | padding: 12px; 3 | button { 4 | margin: 5px; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/app/components/image-editor/image-editor.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ImageEditorComponent } from './image-editor.component'; 4 | 5 | describe('ImageEditorComponent', () => { 6 | let component: ImageEditorComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ImageEditorComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ImageEditorComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/image-editor/image-editor.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-image-editor', 5 | templateUrl: './image-editor.component.html', 6 | styleUrls: ['./image-editor.component.scss'] 7 | }) 8 | export class ImageEditorComponent implements OnInit { 9 | 10 | /** 11 | * an array of action buttons displayed on the editor screen 12 | */ 13 | private editorButtons: Array = [ 14 | { 15 | name: 'exit', 16 | action: this.exit, 17 | icon: 'arrow_back', 18 | type: 'fab', 19 | mode: 'crop' 20 | }, 21 | { 22 | name: 'rotate', 23 | action: function() {}, 24 | icon: 'rotate_right', 25 | type: 'fab', 26 | mode: 'crop' 27 | }, 28 | { 29 | name: 'done_crop', 30 | action: () => { 31 | this.mode = 'color'; 32 | }, 33 | icon: 'done', 34 | type: 'fab', 35 | mode: 'crop' 36 | }, 37 | { 38 | name: 'back', 39 | action: () => { 40 | this.mode = 'color'; 41 | }, 42 | icon: 'arrow_back', 43 | type: 'fab', 44 | mode: 'color' 45 | }, 46 | { 47 | name: 'filter', 48 | action: () => { 49 | return this.applyFilter('default'); 50 | }, 51 | icon: 'photo_filter', 52 | type: 'fab', 53 | mode: 'color' 54 | }, 55 | { 56 | name: 'upload', 57 | action: this.exit, 58 | icon: 'cloud_upload', 59 | type: 'fab', 60 | mode: 'color' 61 | }, 62 | ]; 63 | get displayedButtons() { 64 | return this.editorButtons.filter(button => { 65 | return button.mode === this.mode; 66 | }); 67 | } 68 | private maxDisplayWidth: 800; 69 | imageDivStyle: {'background-image': string; width: string; height: string; }; 70 | imageLoaded = false; 71 | mode: 'crop'|'color'; 72 | private screenDimensions: ImageDimensions; 73 | private imageDimensions: ImageDimensions; 74 | private imageResizeRatio: number; 75 | private originalImage: HTMLCanvasElement; 76 | private editedImage: HTMLCanvasElement; 77 | private _file: File; 78 | @Output() error: EventEmitter = new EventEmitter(); 79 | @Output() exitEditor: EventEmitter = new EventEmitter(); 80 | @Output() editResult: EventEmitter = new EventEmitter(); 81 | @Input() 82 | set file(image: File) { 83 | this._file = image; 84 | this.readImage() 85 | .then(() => { 86 | 87 | }) 88 | .catch(err => { 89 | console.error(err); 90 | this.error.emit(err); 91 | }); 92 | } 93 | @Input() maxImageWidth: number; 94 | @Input() buttonThemeColor: 'primary'|'warn'|'accent' = 'accent'; 95 | 96 | constructor() { 97 | this.screenDimensions = { 98 | width: window.innerWidth, 99 | height: window.innerHeight 100 | }; 101 | } 102 | 103 | ngOnInit() { 104 | this.mode = 'crop'; 105 | } 106 | 107 | exit() { 108 | this.exitEditor.emit('canceled'); 109 | } 110 | 111 | applyFilter(filter) { 112 | 113 | } 114 | 115 | saveImage() { 116 | this.editResult.emit(this.editedImage); 117 | } 118 | 119 | resizeImage() { 120 | 121 | } 122 | 123 | readFile() { 124 | return new Promise((resolve, reject) => { 125 | const reader = new FileReader(); 126 | reader.onload = (event) => { 127 | console.log(event); 128 | resolve(reader.result); 129 | }; 130 | reader.onerror = (err) => { 131 | reject(err); 132 | }; 133 | reader.readAsDataURL(this._file); 134 | }); 135 | } 136 | 137 | readImage() { 138 | return new Promise(async (resolve, reject) => { 139 | let imageSrc; 140 | try { 141 | imageSrc = await this.readFile(); 142 | } catch (err) { 143 | reject(err); 144 | } 145 | const img = new Image(); 146 | img.onload = () => { 147 | const canvasDimensions = this.calculateDimensions(img.width, img.height); 148 | this.imageResizeRatio = canvasDimensions.ratio; 149 | 150 | this.originalImage = document.createElement('canvas'); 151 | this.originalImage.width = canvasDimensions.width; 152 | this.originalImage.height = canvasDimensions.height; 153 | this.imageDimensions.width = canvasDimensions.width; 154 | this.imageDimensions.height = canvasDimensions.height; 155 | const ctx = this.originalImage.getContext('2d'); 156 | ctx.drawImage(img, 0, 0); 157 | resolve(); 158 | }; 159 | img.src = imageSrc; 160 | }); 161 | } 162 | 163 | calculateDimensions(width: number, height: number): { width: number; height: number; ratio: number} { 164 | const ratio = width / height; 165 | const maxWidth = this.screenDimensions.width < this.maxDisplayWidth ? this.screenDimensions.width = 30 : this.maxDisplayWidth; 166 | if (width > this.maxDisplayWidth) { 167 | return { 168 | width: maxWidth, 169 | height: maxWidth * ratio, 170 | ratio: maxWidth / ratio 171 | }; 172 | } 173 | } 174 | 175 | 176 | } 177 | 178 | 179 | export interface ImageDimensions { 180 | width: number; 181 | height: number; 182 | } 183 | 184 | export interface EditorActionButton { 185 | name: string; 186 | type: 'button'|'fab'; 187 | icon: string; 188 | action: Function; 189 | text?: string; 190 | mode: 'crop'|'color'; 191 | } 192 | -------------------------------------------------------------------------------- /src/app/components/shape-outine/shape-outine.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/app/components/shape-outine/shape-outine.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/src/app/components/shape-outine/shape-outine.component.scss -------------------------------------------------------------------------------- /src/app/components/shape-outine/shape-outine.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ShapeOutineComponent } from './shape-outine.component'; 4 | 5 | describe('ShapeOutineComponent', () => { 6 | let component: ShapeOutineComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ShapeOutineComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ShapeOutineComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/app/components/shape-outine/shape-outine.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, Input, OnInit, ViewChild} from '@angular/core'; 2 | import {LimitsService, PointPositionChange} from '../../services/limits.service'; 3 | 4 | @Component({ 5 | selector: 'app-shape-outine', 6 | templateUrl: './shape-outine.component.html', 7 | styleUrls: ['./shape-outine.component.scss'] 8 | }) 9 | export class ShapeOutineComponent implements AfterViewInit { 10 | 11 | @Input() color = '#3cabe2'; 12 | @Input() weight: number; 13 | @ViewChild('outline') canvas; 14 | 15 | private _points: Array; 16 | private _sortedPoints: Array; 17 | constructor(private limitisService: LimitsService) {} 18 | 19 | ngAfterViewInit() { 20 | this.limitisService.positions.subscribe(positions => { 21 | this._points = positions; 22 | this.sortPoints(); 23 | this.drawShape(); 24 | }); 25 | } 26 | 27 | sortPoints() { 28 | const _points = Array.from(this._points); 29 | const sortedPoints = []; 30 | 31 | const sortOrder = { 32 | vertical: ['top', 'top', 'bottom', 'bottom'], 33 | horizontal: ['left', 'right', 'right', 'left'] 34 | } 35 | 36 | for (let i = 0; i < 4; i++) { 37 | const roles = Array.from([sortOrder.vertical[i], sortOrder.horizontal[i]]); 38 | sortedPoints.push(_points.filter((point) => { 39 | return this.limitisService.compareArray(point.roles, roles) 40 | })[0]); 41 | console.log(roles); 42 | console.log(_points.filter((point) => { 43 | return this.limitisService.compareArray(point.roles, roles) 44 | })[0].roles); 45 | } 46 | this._sortedPoints = sortedPoints; 47 | } 48 | 49 | drawShape() { 50 | const canvas = this.canvas.nativeElement; 51 | const ctx = canvas.getContext('2d'); 52 | ctx.clearRect(0, 0, canvas.width, canvas.height); 53 | ctx.lineWidth = this.weight; 54 | ctx.strokeStyle = this.color; 55 | 56 | this._sortedPoints.forEach((point, index) => { 57 | const nextPoint = this._sortedPoints[index + 1]; 58 | ctx.beginPath(); 59 | ctx.moveTo(point.x + 2, point.y + 2); 60 | if (index === this._sortedPoints.length - 1) { 61 | ctx.lineTo(this._sortedPoints[0].x + 2, this._points[0].y + 2); 62 | } else { 63 | ctx.lineTo(nextPoint.x + 2, nextPoint.y + 2); 64 | } 65 | ctx.stroke(); 66 | }); 67 | } 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/app/services/limits.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { LimitsService } from './limits.service'; 4 | 5 | describe('LimitsService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: LimitsService = TestBed.get(LimitsService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/app/services/limits.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import {BehaviorSubject} from 'rxjs'; 3 | import {Direction, RolesArray} from '../components/draggable-point/draggable-point.component'; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class LimitsService { 9 | 10 | private _limits = { 11 | top: 0, 12 | bottom: 0, 13 | right: 0, 14 | left: 0 15 | }; 16 | private _points: any[] = []; 17 | 18 | private limitDirections: RolesArray = ['left', 'right', 'top', 'bottom']; 19 | 20 | public positions: BehaviorSubject> = new BehaviorSubject>(Array.from(this._points)); 21 | public limits: BehaviorSubject = new BehaviorSubject(this._limits); 22 | 23 | constructor() { 24 | 25 | } 26 | 27 | private getDirectionAxis(direction) { 28 | return { 29 | left: 'x', 30 | right: 'x', 31 | top: 'y', 32 | bottom: 'y' 33 | }[direction]; 34 | } 35 | 36 | /** 37 | * updates limits and point positions and calls next on the observables 38 | * @param positionChangeData - position change event data 39 | */ 40 | positionChange(positionChangeData: PointPositionChange) { 41 | // update positions according to current position change 42 | this.updatePosition(positionChangeData); 43 | 44 | // for each direction: 45 | // 1. filter the _points that have a role as the direction's limit 46 | // 2. for top and left find max x | y values, and min for right and bottom 47 | this.limitDirections.forEach(direction => { 48 | const relevantPoints = this._points.filter(point => { 49 | return point.roles.includes(direction); 50 | }) 51 | .map((point: PointPositionChange) => { 52 | return point[this.getDirectionAxis(direction)]; 53 | }); 54 | let limit; 55 | if (direction === 'top' || direction === 'left') { 56 | limit = Math.max(...relevantPoints); 57 | } 58 | if (direction === 'right' || direction === 'bottom') { 59 | limit = Math.min(...relevantPoints); 60 | } 61 | this._limits[direction] = limit; 62 | }); 63 | 64 | this.limits.next(this._limits); 65 | this.positions.next(Array.from(this._points)); 66 | } 67 | 68 | /** 69 | * updates the position of the point 70 | * @param positionChange - position change event data 71 | */ 72 | updatePosition(positionChange: PointPositionChange) { 73 | // finds the current position of the point by it's roles, than splices it for the new position or pushes it if it's not yet in the array 74 | const index = this._points.findIndex(point => { 75 | return this.compareArray(positionChange.roles, point.roles); 76 | }); 77 | if (index === -1) { 78 | this._points.push(positionChange); 79 | } else { 80 | this._points.splice(index, 1, positionChange); 81 | } 82 | } 83 | 84 | /** 85 | * check if a position change event exceeds the limits 86 | * @param positionChange - position change event data 87 | * @returns LimitException 88 | */ 89 | exceedsLimit(positionChange: PointPositionChange): LimitException { 90 | const pointLimits = this.limitDirections.filter(direction => { 91 | return !positionChange.roles.includes(direction); 92 | }); 93 | 94 | const limitException: LimitException = { 95 | exceeds: false, 96 | resetCoefficients: { 97 | x: 0, 98 | y: 0 99 | }, 100 | resetCoordinates: { 101 | x: positionChange.x, 102 | y: positionChange.y 103 | } 104 | }; 105 | 106 | // limit directions are the opposite sides of the point's roles 107 | pointLimits.forEach(direction => { 108 | const directionAxis = this.getDirectionAxis(direction); 109 | if (direction === 'top' || direction === 'left') { 110 | if (positionChange[directionAxis] < this._limits[direction]) { 111 | limitException.resetCoefficients[directionAxis] = 1; 112 | limitException.resetCoordinates[directionAxis] = this._limits[direction]; 113 | } 114 | } else if (direction === 'right' || direction === 'bottom') { 115 | if (positionChange[directionAxis] > this._limits[direction]) { 116 | limitException.resetCoefficients[directionAxis] = -1; 117 | limitException.resetCoordinates[directionAxis] = this._limits[direction]; 118 | } 119 | } 120 | }); 121 | 122 | if (limitException.resetCoefficients.x !== 0 || limitException.resetCoefficients.y !== 0) { 123 | limitException.exceeds = true; 124 | } 125 | 126 | return limitException; 127 | } 128 | 129 | /** 130 | * checks if two array contain the same values 131 | * @param array1 - array 1 132 | * @param array2 - array 2 133 | * @returns boolean 134 | */ 135 | compareArray(array1: Array, array2: Array): boolean { 136 | return array1.every((element) => { 137 | return array2.includes(element); 138 | }) && array1.length === array2.length; 139 | } 140 | 141 | rotateClockwise(corner: PointPositionChange): PointPositionChange { 142 | const rotated: PointPositionChange = { 143 | x: corner.y, 144 | y: corner.x, 145 | roles: [] 146 | }; 147 | corner.roles.forEach(direction => { 148 | rotated.roles.push(this.nextSquareSide(direction)); 149 | }); 150 | return rotated; 151 | } 152 | 153 | private nextSquareSide(currentDirection): Direction { 154 | const order: RolesArray = ['left', 'top', 'right', 'bottom']; 155 | const index = order.findIndex(item => { 156 | return item === currentDirection; 157 | }); 158 | if (index === -1) { 159 | throw Error('invalid direction'); 160 | } else if (index === order.length - 1) { 161 | return order[0]; 162 | } else { 163 | return order[index + 1]; 164 | } 165 | } 166 | } 167 | 168 | 169 | export interface PointPositionChange { 170 | x: number; 171 | y: number; 172 | roles: RolesArray; 173 | } 174 | 175 | export interface AreaLimits { 176 | top: number; 177 | bottom: number; 178 | right: number; 179 | left: number; 180 | } 181 | 182 | export interface LimitException { 183 | exceeds: boolean; 184 | resetCoefficients: { 185 | x: 1 | 0 | -1 186 | y: 1 | 0 | -1 187 | }; 188 | resetCoordinates: { 189 | x: number; 190 | y: number; 191 | }; 192 | } 193 | 194 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roiperlman/ngx-document-scanner/a28ca00bac34da08f4a66328810697da60289f92/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DocScanner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es5", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2018", 18 | "dom" 19 | ], 20 | "paths": { 21 | "ngx-document-scanner": [ 22 | "dist/ngx-document-scanner" 23 | ], 24 | "ngx-document-scanner/*": [ 25 | "dist/ngx-document-scanner/*" 26 | ], 27 | "ngx-opencv": [ 28 | "dist/ngx-opencv" 29 | ], 30 | "ngx-opencv/*": [ 31 | "dist/ngx-opencv/*" 32 | ] 33 | } 34 | }, 35 | "types": [ 36 | "src/typings.d.ts" 37 | ] 38 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs/Rx" 22 | ], 23 | "import-spacing": true, 24 | "indent": [ 25 | true, 26 | "spaces" 27 | ], 28 | "interface-over-type-literal": true, 29 | "label-position": true, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-arg": true, 47 | "no-bitwise": true, 48 | "no-console": [ 49 | true, 50 | "debug", 51 | "info", 52 | "time", 53 | "timeEnd", 54 | "trace" 55 | ], 56 | "no-construct": true, 57 | "no-debugger": true, 58 | "no-duplicate-super": true, 59 | "no-empty": false, 60 | "no-empty-interface": true, 61 | "no-eval": true, 62 | "no-inferrable-types": [ 63 | true, 64 | "ignore-params" 65 | ], 66 | "no-misused-new": true, 67 | "no-non-null-assertion": true, 68 | "no-redundant-jsdoc": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-use-before-declare": true, 77 | "no-var-keyword": true, 78 | "object-literal-sort-keys": false, 79 | "one-line": [ 80 | true, 81 | "check-open-brace", 82 | "check-catch", 83 | "check-else", 84 | "check-whitespace" 85 | ], 86 | "prefer-const": true, 87 | "quotemark": [ 88 | true, 89 | "single" 90 | ], 91 | "radix": true, 92 | "semicolon": [ 93 | true, 94 | "always" 95 | ], 96 | "triple-equals": [ 97 | true, 98 | "allow-null-check" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "unified-signatures": true, 111 | "variable-name": false, 112 | "whitespace": [ 113 | true, 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type" 119 | ], 120 | "no-output-on-prefix": true, 121 | "use-input-property-decorator": true, 122 | "use-output-property-decorator": true, 123 | "use-host-property-decorator": true, 124 | "no-input-rename": true, 125 | "no-output-rename": true, 126 | "use-life-cycle-interface": true, 127 | "use-pipe-transform-interface": true, 128 | "component-class-suffix": true, 129 | "directive-class-suffix": true 130 | } 131 | } 132 | --------------------------------------------------------------------------------